Compare commits
66 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
79d7291906 | ||
|
bae4eb1830 | ||
|
609d4ae69d | ||
|
99c3a88df4 | ||
|
4364c6ebb2 | ||
|
73b1e63bd7 | ||
|
b7856ce26a | ||
|
f5eefe51f8 | ||
|
aac6694127 | ||
|
6e499300ff | ||
|
d3b702e1ad | ||
|
eb1d1726b9 | ||
|
8f7632bc7f | ||
|
d2e66a8d82 | ||
|
4b4cd06be2 | ||
|
a821f6c555 | ||
|
fbeef4542d | ||
|
f02fbfc10c | ||
|
e4c93e8c59 | ||
|
35bd01e3f2 | ||
|
040f0f3e7c | ||
|
af09213d1a | ||
|
538cb2e940 | ||
|
45f62c424c | ||
|
c33e255182 | ||
|
0f43977bb2 | ||
|
9b75493988 | ||
|
0faaabb78c | ||
|
e510c72111 | ||
|
57d41f3e35 | ||
|
e9d880a105 | ||
|
221510cfcf | ||
|
2636a4fbed | ||
|
7b2140d7f9 | ||
|
3042bac0bb | ||
|
5af85c5ef6 | ||
|
0f5d3ae8f0 | ||
|
39b45c65c1 | ||
|
948241b659 | ||
|
2cf795c118 | ||
|
9eb00949ae | ||
|
2e908bfdee | ||
|
cefd6ccb96 | ||
|
e280a577ae | ||
|
246005cc84 | ||
|
4ca2ea3466 | ||
|
4de0721a24 | ||
|
4ef24028be | ||
|
a353a11bf4 | ||
|
4a7936aa24 | ||
|
f51c2d004d | ||
|
e43a8b28d6 | ||
|
cf94d35b51 | ||
|
ebc56fee59 | ||
|
2120285467 | ||
|
765ff8463e | ||
|
2d869489ef | ||
|
909c14117f | ||
|
9a149c83ff | ||
|
2321165d53 | ||
|
d1a49a4f0b | ||
|
793220faf8 | ||
|
996f2a03d5 | ||
|
877a2838a6 | ||
|
1ab785b7a3 | ||
|
06c708053f |
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"browser": true,
|
||||
"shadow": true,
|
||||
"extends": "./node_modules/fbjs-scripts/eslint/.eslintrc",
|
||||
"globals": {
|
||||
"jasmine": true,
|
||||
"describe": true,
|
||||
@@ -17,8 +16,5 @@
|
||||
"console": true,
|
||||
"setTimeout": true,
|
||||
"define": true
|
||||
},
|
||||
"rules": {
|
||||
"quotes": [2, "single"]
|
||||
}
|
||||
}
|
||||
|
33
Gruntfile.js
33
Gruntfile.js
@@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(grunt) {
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var isWindows = /^win/.test(process.platform);
|
||||
var isWindows = (/^win/).test(process.platform);
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
@@ -25,9 +24,8 @@ module.exports = function(grunt) {
|
||||
config.cTestOutput = 'c_test.exe';
|
||||
config.cTestCompile = 'cl -nologo -Zi -Tpsrc/__tests__/Layout-test.c -Tpsrc/Layout.c -Tpsrc/Layout-test-utils.c -link -incremental:no -out:"<%= config.cTestOutput %>"';
|
||||
config.cTestExecute = '<%= config.cTestOutput %>';
|
||||
config.cTestClean = ['<%= config.cTestOutput %>','*.obj','*.pdb'];
|
||||
}
|
||||
else {
|
||||
config.cTestClean = ['<%= config.cTestOutput %>', '*.obj', '*.pdb'];
|
||||
} else {
|
||||
// GCC build (OSX, Linux, ...), assumes gcc is in the path.
|
||||
config.cTestOutput = 'c_test';
|
||||
config.cTestCompile = 'gcc -std=c99 -Werror -Wno-padded src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c -lm -o "./<%= config.cTestOutput %>"';
|
||||
@@ -42,12 +40,12 @@ module.exports = function(grunt) {
|
||||
dist: {
|
||||
options: {
|
||||
create: ['<%= config.distFolder %>']
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clean: {
|
||||
dist: ['<%= config.distFolder %>'],
|
||||
dist: ['<%= config.distFolder %>/css-layout.*'],
|
||||
cTest: config.cTestClean,
|
||||
javaTest: ['**/*.class']
|
||||
},
|
||||
@@ -56,12 +54,12 @@ module.exports = function(grunt) {
|
||||
options: {
|
||||
configFile: '.eslintrc'
|
||||
},
|
||||
target: ['<%= config.srcFolder %>/Layout.js']
|
||||
target: ['<%= config.srcFolder %>/**/*.js', './Gruntfile.js']
|
||||
},
|
||||
|
||||
includereplace: {
|
||||
options: {
|
||||
prefix: '// @@',
|
||||
prefix: '// @@'
|
||||
},
|
||||
main: {
|
||||
src: '<%= config.srcFolder %>/<%= config.libName %>.js',
|
||||
@@ -120,17 +118,16 @@ module.exports = function(grunt) {
|
||||
'#ifdef CSS_LAYOUT_IMPLEMENTATION',
|
||||
src,
|
||||
'#endif // CSS_LAYOUT_IMPLEMENTATION'
|
||||
].join('\n')
|
||||
}
|
||||
else {
|
||||
].join('\n');
|
||||
} else {
|
||||
return src;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
dist: {
|
||||
src: ['<%= config.srcFolder %>/Layout.h', '<%= config.srcFolder %>/Layout.c'],
|
||||
dest: '<%= config.distFolder %>/css-layout.h',
|
||||
},
|
||||
dest: '<%= config.distFolder %>/css-layout.h'
|
||||
}
|
||||
},
|
||||
|
||||
shell: {
|
||||
@@ -153,8 +150,8 @@ module.exports = function(grunt) {
|
||||
|
||||
watch: {
|
||||
files: ['src/Layout.js'],
|
||||
tasks: ['ci'],
|
||||
},
|
||||
tasks: ['ci']
|
||||
}
|
||||
});
|
||||
|
||||
// Compiles and runs the Java tests
|
||||
|
11
README.md
11
README.md
@@ -7,7 +7,7 @@ The Java, C and JavaScript version of the code is available via [npm](https://ww
|
||||
|
||||
In order to make sure that the code is correct, it is developed in JavaScript using TDD where each commit adds a unit test and the associated code to make it work. All the unit tests are tested against Chrome's implementation of CSS.
|
||||
|
||||
The JavaScript version has been implemented in a way that can be easily transpiled to C and Java via regexes. The layout function doesn't do any allocation nor uses any of the dynamic aspect of JavaScript. The tests are also transpiled to make sure that the implementations are correct everywhere.
|
||||
The JavaScript version has been implemented in a way that can be easily transpiled to C and Java via regexes. The layout function doesn't do any allocation nor uses any of the dynamic aspects of JavaScript. The tests are also transpiled to make sure that the implementations are correct everywhere.
|
||||
|
||||
|
||||
Usage
|
||||
@@ -91,7 +91,7 @@ padding, paddingLeft, paddingRight, paddingTop, paddingBottom | positive number
|
||||
borderWidth, borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth | positive number
|
||||
flexDirection | 'column', 'row'
|
||||
justifyContent | 'flex-start', 'center', 'flex-end', 'space-between', 'space-around'
|
||||
alignItems, alignSelf, alignContent | 'flex-start', 'center', 'flex-end', 'stretch'
|
||||
alignItems, alignSelf | 'flex-start', 'center', 'flex-end', 'stretch'
|
||||
flex | positive number
|
||||
flexWrap | 'wrap', 'nowrap'
|
||||
position | 'relative', 'absolute'
|
||||
@@ -126,6 +126,13 @@ div, span {
|
||||
- All the flex elements are oriented from top to bottom, left to right and do not shrink. This is how things are laid out using the default CSS settings and what you'd expect.
|
||||
- Everything is `position: relative`. This makes `position: absolute` target the direct parent and not some parent which is either `relative` or `absolute`. If you want to position an element relative to something else, you should move it in the DOM instead of relying of CSS. It also makes `top, left, right, bottom` do something when not specifying `position: absolute`.
|
||||
|
||||
Native Usage Notes
|
||||
------------------
|
||||
|
||||
The C equivalent of `computeLayout` is [`layoutNode`](dist/css-layout.h#L1378).
|
||||
|
||||
In order for layout to properly layout reflowable text, the `measure` function must be set on the `css_node` structure. The property can be found in [`css-layout.h`](dist/css-layout.h#L146). This function must take a void pointer to a `context` that will affect the size of the node and the `width` as computed by the layout engine, and must return a `css_dim_t` structure defining the actual needed size of the node. For the most part, the `context` field can be the text inside the node. No C implementation of this function is provided in provided - it depends on your use of css-layout. However an implementation of the function in JavaScript can be used for reference in the [test utilities](src/Layout-test-utils.js#L383).
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
|
5
dist/README.md
vendored
5
dist/README.md
vendored
@@ -6,6 +6,9 @@ Releases can be found on [npm](https://www.npmjs.com/package/css-layout).
|
||||
# Ensure that the local codebase is up to date
|
||||
git fetch upstream master && git checkout FETCH_HEAD
|
||||
|
||||
# build the latest version of the library
|
||||
grunt
|
||||
|
||||
# increment the version number and tag the release
|
||||
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease]
|
||||
|
||||
@@ -14,4 +17,4 @@ git push --tags upstream HEAD:master
|
||||
|
||||
# publish this new version to npm
|
||||
npm publish
|
||||
```
|
||||
```
|
||||
|
492
dist/css-layout.h
vendored
492
dist/css-layout.h
vendored
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* #define CSS_LAYOUT_IMPLEMENTATION
|
||||
* before you include this file in *one* C or C++ file to create the implementation.
|
||||
*/
|
||||
/*
|
||||
* #define CSS_LAYOUT_IMPLEMENTATION
|
||||
* before you include this file in *one* C or C++ file to create the implementation.
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
@@ -133,18 +133,22 @@ typedef struct {
|
||||
float maxDimensions[2];
|
||||
} css_style_t;
|
||||
|
||||
typedef struct css_node {
|
||||
typedef struct css_node css_node_t;
|
||||
struct css_node {
|
||||
css_style_t style;
|
||||
css_layout_t layout;
|
||||
int children_count;
|
||||
int line_index;
|
||||
|
||||
css_node_t* next_absolute_child;
|
||||
css_node_t* next_flex_child;
|
||||
|
||||
css_dim_t (*measure)(void *context, float width);
|
||||
void (*print)(void *context);
|
||||
struct css_node* (*get_child)(void *context, int i);
|
||||
bool (*is_dirty)(void *context);
|
||||
void *context;
|
||||
} css_node_t;
|
||||
};
|
||||
|
||||
|
||||
// Lifecycle of nodes and children
|
||||
@@ -165,7 +169,7 @@ void layoutNode(css_node_t *node, float maxWidth, css_direction_t parentDirectio
|
||||
bool isUndefined(float value);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CSS_LAYOUT_IMPLEMENTATION
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
@@ -545,18 +549,6 @@ static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis
|
||||
return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis);
|
||||
}
|
||||
|
||||
static css_position_type_t getPositionType(css_node_t *node) {
|
||||
return node->style.position_type;
|
||||
}
|
||||
|
||||
static css_justify_t getJustifyContent(css_node_t *node) {
|
||||
return node->style.justify_content;
|
||||
}
|
||||
|
||||
static css_align_t getAlignContent(css_node_t *node) {
|
||||
return node->style.align_content;
|
||||
}
|
||||
|
||||
static css_align_t getAlignItem(css_node_t *node, css_node_t *child) {
|
||||
if (child->style.align_self != CSS_ALIGN_AUTO) {
|
||||
return child->style.align_self;
|
||||
@@ -604,7 +596,7 @@ static float getFlex(css_node_t *node) {
|
||||
|
||||
static bool isFlex(css_node_t *node) {
|
||||
return (
|
||||
getPositionType(node) == CSS_POSITION_RELATIVE &&
|
||||
node->style.position_type == CSS_POSITION_RELATIVE &&
|
||||
getFlex(node) > 0
|
||||
);
|
||||
}
|
||||
@@ -621,7 +613,7 @@ static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
|
||||
|
||||
static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) {
|
||||
float value = node->style.dimensions[dim[axis]];
|
||||
return !isUndefined(value) && value > 0.0;
|
||||
return !isUndefined(value) && value >= 0.0;
|
||||
}
|
||||
|
||||
static bool isPosDefined(css_node_t *node, css_position_t position) {
|
||||
@@ -722,23 +714,29 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) +
|
||||
getRelativePosition(node, crossAxis);
|
||||
|
||||
// Inline immutable values from the target node to avoid excessive method
|
||||
// invocations during the layout calculation.
|
||||
int childCount = node->children_count;
|
||||
float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
|
||||
if (isMeasureDefined(node)) {
|
||||
bool isResolvedRowDimDefined = !isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]);
|
||||
|
||||
float width = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
width = node->style.dimensions[CSS_WIDTH];
|
||||
} else if (!isUndefined(node->layout.dimensions[dim[resolvedRowAxis]])) {
|
||||
} else if (isResolvedRowDimDefined) {
|
||||
width = node->layout.dimensions[dim[resolvedRowAxis]];
|
||||
} else {
|
||||
width = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis);
|
||||
}
|
||||
width -= getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
width -= paddingAndBorderAxisResolvedRow;
|
||||
|
||||
// We only need to give a dimension for the text if we haven't got any
|
||||
// for it computed yet. It can either be from the style attribute or because
|
||||
// the element is flexible.
|
||||
bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) &&
|
||||
isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]);
|
||||
bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
|
||||
bool isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&
|
||||
isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
|
||||
|
||||
@@ -751,66 +749,42 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
if (isColumnUndefined) {
|
||||
node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
|
||||
}
|
||||
}
|
||||
if (node->children_count == 0) {
|
||||
if (childCount == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool isNodeFlexWrap = isFlexWrap(node);
|
||||
|
||||
css_justify_t justifyContent = node->style.justify_content;
|
||||
|
||||
float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);
|
||||
float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);
|
||||
float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);
|
||||
float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);
|
||||
|
||||
bool isMainDimDefined = !isUndefined(node->layout.dimensions[dim[mainAxis]]);
|
||||
bool isCrossDimDefined = !isUndefined(node->layout.dimensions[dim[crossAxis]]);
|
||||
bool isMainRowDirection = isRowDirection(mainAxis);
|
||||
|
||||
int i;
|
||||
int ii;
|
||||
css_node_t* child;
|
||||
css_flex_direction_t axis;
|
||||
|
||||
// Pre-fill some dimensions straight from the parent
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||
// we call the recursive layout pass
|
||||
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
|
||||
getPositionType(child) == CSS_POSITION_RELATIVE &&
|
||||
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
|
||||
!isDimDefined(child, crossAxis)) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
} else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
css_node_t* firstAbsoluteChild = NULL;
|
||||
css_node_t* currentAbsoluteChild = NULL;
|
||||
|
||||
float definedMainDim = CSS_UNDEFINED;
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
definedMainDim = node->layout.dimensions[dim[mainAxis]] -
|
||||
getPaddingAndBorderAxis(node, mainAxis);
|
||||
if (isMainDimDefined) {
|
||||
definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
|
||||
}
|
||||
|
||||
// We want to execute the next two loops one per line with flex-wrap
|
||||
@@ -822,7 +796,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
float linesCrossDim = 0;
|
||||
float linesMainDim = 0;
|
||||
int linesCount = 0;
|
||||
while (endLine < node->children_count) {
|
||||
while (endLine < childCount) {
|
||||
// <Loop A> Layout non flexible children and count children by type
|
||||
|
||||
// mainContentDim is accumulation of the dimensions and margin of all the
|
||||
@@ -837,16 +811,99 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
float totalFlexible = 0;
|
||||
int nonFlexibleChildrenCount = 0;
|
||||
|
||||
// Use the line loop to position children in the main axis for as long
|
||||
// as they are using a simple stacking behaviour. Children that are
|
||||
// immediately stacked in the initial loop will not be touched again
|
||||
// in <Loop C>.
|
||||
bool isSimpleStackMain =
|
||||
(isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) ||
|
||||
(!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER);
|
||||
int firstComplexMain = (isSimpleStackMain ? childCount : startLine);
|
||||
|
||||
// Use the initial line loop to position children in the cross axis for
|
||||
// as long as they are relatively positioned with alignment STRETCH or
|
||||
// FLEX_START. Children that are immediately stacked in the initial loop
|
||||
// will not be touched again in <Loop D>.
|
||||
bool isSimpleStackCross = true;
|
||||
int firstComplexCross = childCount;
|
||||
|
||||
css_node_t* firstFlexChild = NULL;
|
||||
css_node_t* currentFlexChild = NULL;
|
||||
|
||||
float mainDim = leadingPaddingAndBorderMain;
|
||||
float crossDim = 0;
|
||||
|
||||
float maxWidth;
|
||||
for (i = startLine; i < node->children_count; ++i) {
|
||||
for (i = startLine; i < childCount; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
child->line_index = linesCount;
|
||||
|
||||
child->next_absolute_child = NULL;
|
||||
child->next_flex_child = NULL;
|
||||
|
||||
css_align_t alignItem = getAlignItem(node, child);
|
||||
|
||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||
// we call the recursive layout pass
|
||||
if (alignItem == CSS_ALIGN_STRETCH &&
|
||||
child->style.position_type == CSS_POSITION_RELATIVE &&
|
||||
isCrossDimDefined &&
|
||||
!isDimDefined(child, crossAxis)) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
|
||||
paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
} else if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
|
||||
// Store a private linked list of absolutely positioned children
|
||||
// so that we can efficiently traverse them later.
|
||||
if (firstAbsoluteChild == NULL) {
|
||||
firstAbsoluteChild = child;
|
||||
}
|
||||
if (currentAbsoluteChild != NULL) {
|
||||
currentAbsoluteChild->next_absolute_child = child;
|
||||
}
|
||||
currentAbsoluteChild = child;
|
||||
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float nextContentDim = 0;
|
||||
|
||||
// It only makes sense to consider a child flexible if we have a computed
|
||||
// dimension for the node->
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) {
|
||||
if (isMainDimDefined && isFlex(child)) {
|
||||
flexibleChildrenCount++;
|
||||
totalFlexible += getFlex(child);
|
||||
totalFlexible += child->style.flex;
|
||||
|
||||
// Store a private linked list of flexible children so that we can
|
||||
// efficiently traverse them later.
|
||||
if (firstFlexChild == NULL) {
|
||||
firstFlexChild = child;
|
||||
}
|
||||
if (currentFlexChild != NULL) {
|
||||
currentFlexChild->next_flex_child = child;
|
||||
}
|
||||
currentFlexChild = child;
|
||||
|
||||
// Even if we don't know its exact size yet, we already know the padding,
|
||||
// border and margin. We'll use this partial information, which represents
|
||||
@@ -857,14 +914,14 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
} else {
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (!isRowDirection(mainAxis)) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
|
||||
if (!isMainRowDirection) {
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
} else {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,7 +932,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// Absolute positioned elements do not take part of the layout, so we
|
||||
// don't use them to compute mainContentDim
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type == CSS_POSITION_RELATIVE) {
|
||||
nonFlexibleChildrenCount++;
|
||||
// At this point we know the final size and margin of the element.
|
||||
nextContentDim = getDimWithMargin(child, mainAxis);
|
||||
@@ -883,8 +940,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
}
|
||||
|
||||
// The element we are about to add would make us go to the next line
|
||||
if (isFlexWrap(node) &&
|
||||
!isUndefined(node->layout.dimensions[dim[mainAxis]]) &&
|
||||
if (isNodeFlexWrap &&
|
||||
isMainDimDefined &&
|
||||
mainContentDim + nextContentDim > definedMainDim &&
|
||||
// If there's only one element, then it's bigger than the content
|
||||
// and needs its own line
|
||||
@@ -893,6 +950,44 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
alreadyComputedNextLayout = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable simple stacking in the main axis for the current line as
|
||||
// we found a non-trivial child-> The remaining children will be laid out
|
||||
// in <Loop C>.
|
||||
if (isSimpleStackMain &&
|
||||
(child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) {
|
||||
isSimpleStackMain = false;
|
||||
firstComplexMain = i;
|
||||
}
|
||||
|
||||
// Disable simple stacking in the cross axis for the current line as
|
||||
// we found a non-trivial child-> The remaining children will be laid out
|
||||
// in <Loop D>.
|
||||
if (isSimpleStackCross &&
|
||||
(child->style.position_type != CSS_POSITION_RELATIVE ||
|
||||
(alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) ||
|
||||
isUndefined(child->layout.dimensions[dim[crossAxis]]))) {
|
||||
isSimpleStackCross = false;
|
||||
firstComplexCross = i;
|
||||
}
|
||||
|
||||
if (isSimpleStackMain) {
|
||||
child->layout.position[pos[mainAxis]] += mainDim;
|
||||
if (isMainDimDefined) {
|
||||
setTrailingPosition(node, child, mainAxis);
|
||||
}
|
||||
|
||||
mainDim += getDimWithMargin(child, mainAxis);
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
}
|
||||
|
||||
if (isSimpleStackCross) {
|
||||
child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
|
||||
if (isCrossDimDefined) {
|
||||
setTrailingPosition(node, child, crossAxis);
|
||||
}
|
||||
}
|
||||
|
||||
alreadyComputedNextLayout = 0;
|
||||
mainContentDim += nextContentDim;
|
||||
endLine = i + 1;
|
||||
@@ -908,7 +1003,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// The remaining available space that needs to be allocated
|
||||
float remainingMainDim = 0;
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
if (isMainDimDefined) {
|
||||
remainingMainDim = definedMainDim - mainContentDim;
|
||||
} else {
|
||||
remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;
|
||||
@@ -921,21 +1016,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
float baseMainDim;
|
||||
float boundMainDim;
|
||||
|
||||
// Iterate over every child in the axis. If the flex share of remaining
|
||||
// space doesn't meet min/max bounds, remove this child from flex
|
||||
// calculations.
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (isFlex(child)) {
|
||||
baseMainDim = flexibleMainDim * getFlex(child) +
|
||||
getPaddingAndBorderAxis(child, mainAxis);
|
||||
boundMainDim = boundAxis(child, mainAxis, baseMainDim);
|
||||
// If the flex share of remaining space doesn't meet min/max bounds,
|
||||
// remove this child from flex calculations.
|
||||
currentFlexChild = firstFlexChild;
|
||||
while (currentFlexChild != NULL) {
|
||||
baseMainDim = flexibleMainDim * currentFlexChild->style.flex +
|
||||
getPaddingAndBorderAxis(currentFlexChild, mainAxis);
|
||||
boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
|
||||
|
||||
if (baseMainDim != boundMainDim) {
|
||||
remainingMainDim -= boundMainDim;
|
||||
totalFlexible -= getFlex(child);
|
||||
}
|
||||
if (baseMainDim != boundMainDim) {
|
||||
remainingMainDim -= boundMainDim;
|
||||
totalFlexible -= currentFlexChild->style.flex;
|
||||
}
|
||||
|
||||
currentFlexChild = currentFlexChild->next_flex_child;
|
||||
}
|
||||
flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
|
||||
@@ -944,37 +1038,37 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
if (flexibleMainDim < 0) {
|
||||
flexibleMainDim = 0;
|
||||
}
|
||||
// We iterate over the full array and only apply the action on flexible
|
||||
// children. This is faster than actually allocating a new array that
|
||||
// contains only flexible children.
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (isFlex(child)) {
|
||||
// At this point we know the final size of the element in the main
|
||||
// dimension
|
||||
child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis,
|
||||
flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis)
|
||||
);
|
||||
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
} else if (!isRowDirection(mainAxis)) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
}
|
||||
currentFlexChild = firstFlexChild;
|
||||
while (currentFlexChild != NULL) {
|
||||
// At this point we know the final size of the element in the main
|
||||
// dimension
|
||||
currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
|
||||
flexibleMainDim * currentFlexChild->style.flex +
|
||||
getPaddingAndBorderAxis(currentFlexChild, mainAxis)
|
||||
);
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(child, maxWidth, direction);
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
} else if (!isMainRowDirection) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(currentFlexChild, maxWidth, direction);
|
||||
|
||||
child = currentFlexChild;
|
||||
currentFlexChild = currentFlexChild->next_flex_child;
|
||||
child->next_flex_child = NULL;
|
||||
}
|
||||
|
||||
// We use justifyContent to figure out how to allocate the remaining
|
||||
// space available
|
||||
} else {
|
||||
css_justify_t justifyContent = getJustifyContent(node);
|
||||
} else if (justifyContent != CSS_JUSTIFY_FLEX_START) {
|
||||
if (justifyContent == CSS_JUSTIFY_CENTER) {
|
||||
leadingMainDim = remainingMainDim / 2;
|
||||
} else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
|
||||
@@ -1001,15 +1095,12 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
// find their position. In order to do that, we accumulate data in
|
||||
// variables that are also useful to compute the total dimensions of the
|
||||
// container!
|
||||
float crossDim = 0;
|
||||
float mainDim = leadingMainDim +
|
||||
getLeadingPaddingAndBorder(node, mainAxis);
|
||||
mainDim += leadingMainDim;
|
||||
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
for (i = firstComplexMain; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
child->line_index = linesCount;
|
||||
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||
if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
|
||||
isPosDefined(child, leading[mainAxis])) {
|
||||
// In case the child is position absolute and has left/top being
|
||||
// defined, we override the position to whatever the user said
|
||||
@@ -1023,40 +1114,40 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
child->layout.position[pos[mainAxis]] += mainDim;
|
||||
|
||||
// Define the trailing position accordingly.
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
if (isMainDimDefined) {
|
||||
setTrailingPosition(node, child, mainAxis);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we placed the element, we need to update the variables
|
||||
// We only need to do that for relative elements. Absolute elements
|
||||
// do not take part in that phase.
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
// The main dimension is the sum of all the elements dimension plus
|
||||
// the spacing.
|
||||
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
|
||||
// The cross dimension is the max of the elements dimension since there
|
||||
// can only be one element in that cross dimension.
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
// Now that we placed the element, we need to update the variables
|
||||
// We only need to do that for relative elements. Absolute elements
|
||||
// do not take part in that phase.
|
||||
if (child->style.position_type == CSS_POSITION_RELATIVE) {
|
||||
// The main dimension is the sum of all the elements dimension plus
|
||||
// the spacing.
|
||||
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
|
||||
// The cross dimension is the max of the elements dimension since there
|
||||
// can only be one element in that cross dimension.
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float containerCrossAxis = node->layout.dimensions[dim[crossAxis]];
|
||||
if (isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (!isCrossDimDefined) {
|
||||
containerCrossAxis = fmaxf(
|
||||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)),
|
||||
getPaddingAndBorderAxis(node, crossAxis)
|
||||
boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
|
||||
paddingAndBorderAxisCross
|
||||
);
|
||||
}
|
||||
|
||||
// <Loop D> Position elements in the cross axis
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
for (i = firstComplexCross; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||
if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
|
||||
isPosDefined(child, leading[crossAxis])) {
|
||||
// In case the child is absolutely positionned and has a
|
||||
// top/left/bottom/right being set, we override all the previously
|
||||
@@ -1066,20 +1157,22 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
getLeadingMargin(child, crossAxis);
|
||||
|
||||
} else {
|
||||
float leadingCrossDim = getLeadingPaddingAndBorder(node, crossAxis);
|
||||
float leadingCrossDim = leadingPaddingAndBorderCross;
|
||||
|
||||
// For a relative children, we're either using alignItems (parent) or
|
||||
// alignSelf (child) in order to determine the position in the cross axis
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type == CSS_POSITION_RELATIVE) {
|
||||
/*eslint-disable */
|
||||
// This variable is intentionally re-defined as the code is transpiled to a block scope language
|
||||
css_align_t alignItem = getAlignItem(node, child);
|
||||
/*eslint-enable */
|
||||
if (alignItem == CSS_ALIGN_STRETCH) {
|
||||
// You can only stretch if the dimension has not already been set
|
||||
// previously.
|
||||
if (!isDimDefined(child, crossAxis)) {
|
||||
if (isUndefined(child->layout.dimensions[dim[crossAxis]])) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
boundAxis(child, crossAxis, containerCrossAxis -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getMarginAxis(child, crossAxis)),
|
||||
paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
@@ -1088,8 +1181,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
// The remaining space between the parent dimensions+padding and child
|
||||
// dimensions+margin.
|
||||
float remainingCrossDim = containerCrossAxis -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getDimWithMargin(child, crossAxis);
|
||||
paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis);
|
||||
|
||||
if (alignItem == CSS_ALIGN_CENTER) {
|
||||
leadingCrossDim += remainingCrossDim / 2;
|
||||
@@ -1103,7 +1195,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
|
||||
|
||||
// Define the trailing position accordingly.
|
||||
if (!isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (isCrossDimDefined) {
|
||||
setTrailingPosition(node, child, crossAxis);
|
||||
}
|
||||
}
|
||||
@@ -1128,16 +1220,15 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
|
||||
// section 9.4
|
||||
//
|
||||
if (linesCount > 1 &&
|
||||
!isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (linesCount > 1 && isCrossDimDefined) {
|
||||
float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] -
|
||||
getPaddingAndBorderAxis(node, crossAxis);
|
||||
paddingAndBorderAxisCross;
|
||||
float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
|
||||
|
||||
float crossDimLead = 0;
|
||||
float currentLead = getLeadingPaddingAndBorder(node, crossAxis);
|
||||
float currentLead = leadingPaddingAndBorderCross;
|
||||
|
||||
css_align_t alignContent = getAlignContent(node);
|
||||
css_align_t alignContent = node->style.align_content;
|
||||
if (alignContent == CSS_ALIGN_FLEX_END) {
|
||||
currentLead += remainingAlignContentDim;
|
||||
} else if (alignContent == CSS_ALIGN_CENTER) {
|
||||
@@ -1154,9 +1245,9 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// compute the line's height and find the endIndex
|
||||
float lineHeight = 0;
|
||||
for (ii = startIndex; ii < node->children_count; ++ii) {
|
||||
for (ii = startIndex; ii < childCount; ++ii) {
|
||||
child = node->get_child(node->context, ii);
|
||||
if (getPositionType(child) != CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type != CSS_POSITION_RELATIVE) {
|
||||
continue;
|
||||
}
|
||||
if (child->line_index != i) {
|
||||
@@ -1174,7 +1265,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
for (ii = startIndex; ii < endIndex; ++ii) {
|
||||
child = node->get_child(node->context, ii);
|
||||
if (getPositionType(child) != CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type != CSS_POSITION_RELATIVE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1202,33 +1293,39 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// If the user didn't specify a width or height, and it has not been set
|
||||
// by the container, then we set it via the children.
|
||||
if (isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
if (!isMainDimDefined) {
|
||||
node->layout.dimensions[dim[mainAxis]] = fmaxf(
|
||||
// We're missing the last padding at this point to get the final
|
||||
// dimension
|
||||
boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),
|
||||
// We can never assign a width smaller than the padding and borders
|
||||
getPaddingAndBorderAxis(node, mainAxis)
|
||||
paddingAndBorderAxisMain
|
||||
);
|
||||
|
||||
needsMainTrailingPos = true;
|
||||
if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||
mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||
needsMainTrailingPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (!isCrossDimDefined) {
|
||||
node->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)),
|
||||
getPaddingAndBorderAxis(node, crossAxis)
|
||||
boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
|
||||
paddingAndBorderAxisCross
|
||||
);
|
||||
|
||||
needsCrossTrailingPos = true;
|
||||
if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||
crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||
needsCrossTrailingPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
// <Loop F> Set trailing position if necessary
|
||||
if (needsMainTrailingPos || needsCrossTrailingPos) {
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
for (i = 0; i < childCount; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
|
||||
if (needsMainTrailingPos) {
|
||||
@@ -1242,40 +1339,41 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
}
|
||||
|
||||
// <Loop G> Calculate dimensions for absolutely positioned elements
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])
|
||||
),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
currentAbsoluteChild = firstAbsoluteChild;
|
||||
while (currentAbsoluteChild != NULL) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for
|
||||
// the axis are defined (either both left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(currentAbsoluteChild, axis) &&
|
||||
isPosDefined(currentAbsoluteChild, leading[axis]) &&
|
||||
isPosDefined(currentAbsoluteChild, trailing[axis])) {
|
||||
currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] -
|
||||
getBorderAxis(node, axis) -
|
||||
getMarginAxis(currentAbsoluteChild, axis) -
|
||||
getPosition(currentAbsoluteChild, leading[axis]) -
|
||||
getPosition(currentAbsoluteChild, trailing[axis])
|
||||
),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(currentAbsoluteChild, axis)
|
||||
);
|
||||
}
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (isPosDefined(child, trailing[axis]) &&
|
||||
!isPosDefined(child, leading[axis])) {
|
||||
child->layout.position[leading[axis]] =
|
||||
node->layout.dimensions[dim[axis]] -
|
||||
child->layout.dimensions[dim[axis]] -
|
||||
getPosition(child, trailing[axis]);
|
||||
}
|
||||
|
||||
if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
|
||||
!isPosDefined(currentAbsoluteChild, leading[axis])) {
|
||||
currentAbsoluteChild->layout.position[leading[axis]] =
|
||||
node->layout.dimensions[dim[axis]] -
|
||||
currentAbsoluteChild->layout.dimensions[dim[axis]] -
|
||||
getPosition(currentAbsoluteChild, trailing[axis]);
|
||||
}
|
||||
}
|
||||
|
||||
child = currentAbsoluteChild;
|
||||
currentAbsoluteChild = currentAbsoluteChild->next_absolute_child;
|
||||
child->next_absolute_child = NULL;
|
||||
}
|
||||
/** END_GENERATED **/
|
||||
}
|
||||
|
BIN
dist/css-layout.jar
vendored
BIN
dist/css-layout.jar
vendored
Binary file not shown.
759
dist/css-layout.js
vendored
759
dist/css-layout.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/css-layout.min.js
vendored
2
dist/css-layout.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/css-layout.min.js.map
vendored
2
dist/css-layout.min.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "css-layout",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.1",
|
||||
"description": "Reimplementation of CSS layout using pure JavaScript",
|
||||
"main": "dist/css-layout.js",
|
||||
"scripts": {
|
||||
"watch": "grunt watch",
|
||||
"test": "grunt ci",
|
||||
"postversion": "git push --tags upstream HEAD:master"
|
||||
"test": "grunt ci"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,6 +23,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/facebook/css-layout",
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^4.1.3",
|
||||
"fbjs-scripts": "^0.2.2",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-contrib-clean": "^0.6.0",
|
||||
|
175
src/CSharpTranspiler.js
Normal file
175
src/CSharpTranspiler.js
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
function __transpileToCSharpCommon(code) {
|
||||
return code
|
||||
.replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED')
|
||||
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
|
||||
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
|
||||
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
|
||||
.replace(/css_flex_direction_t/g, 'CSSFlexDirection')
|
||||
.replace(/css_direction_t/g, 'CSSDirection')
|
||||
.replace(/css_align_t/g, 'CSSAlign')
|
||||
.replace(/css_justify_t/g, 'CSSJustify')
|
||||
.replace(/css_dim_t/g, 'MeasureOutput')
|
||||
.replace(/bool/g, 'boolean')
|
||||
.replace(/style\[dim/g, 'style.dimensions[dim')
|
||||
.replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]')
|
||||
.replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]')
|
||||
.replace(/layout\[dim/g, 'layout.dimensions[dim')
|
||||
.replace(/layout\[pos/g, 'layout.position[pos')
|
||||
.replace(/layout\[leading/g, 'layout.position[leading')
|
||||
.replace(/layout\[trailing/g, 'layout.position[trailing')
|
||||
.replace(/getPositionType\((.+?)\)/g, '$1.style.positionType')
|
||||
.replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent')
|
||||
.replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent')
|
||||
.replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)')
|
||||
.replace(/isDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] >= 0.0\)')
|
||||
.replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)')
|
||||
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]')
|
||||
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)')
|
||||
.replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)')
|
||||
.replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)')
|
||||
.replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)')
|
||||
.replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
|
||||
.replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)')
|
||||
.replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)')
|
||||
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
|
||||
.replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||
.replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||
.replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||
.replace(/getTrailingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||
.replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||
.replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||
.replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)')
|
||||
.replace(/isUndefined\((.+?)\)/g, 'float.IsNaN\($1\)')
|
||||
.replace(/\/\*\(c\)!([^*]+)\*\//g, '')
|
||||
.replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||
|
||||
// additional case conversions
|
||||
|
||||
.replace(/(CSSConstants|CSSWrap|CSSJustify|CSSAlign|CSSPositionType)\.([_A-Z]+)/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.' + constantToPascalCase(match2);
|
||||
});
|
||||
}
|
||||
|
||||
function __transpileSingleTestToCSharp(code) {
|
||||
return __transpileToCSharpCommon(code)
|
||||
.replace(/CSS_DIRECTION_/g, 'CSSDirection.')
|
||||
.replace(/CSS_FLEX_DIRECTION_/g, 'CSSFlexDirection.')
|
||||
.replace(/CSS_WRAP/g, 'CSSWrap.WRAP')
|
||||
.replace(/new_test_css_node/g, 'new TestCSSNode')
|
||||
.replace(// style.position[CSS_TOP] => style.position[CSSLayout.POSITION_TOP]
|
||||
/(style|layout)\.position\[CSS_(LEFT|TOP|RIGHT|BOTTOM)\]/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.position[POSITION_' + match2 + ']';
|
||||
})
|
||||
.replace(// style.dimensions[CSS_WIDTH] => style.dimensions[CSSLayout.DIMENSION_WIDTH]
|
||||
/(style|layout)\.dimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.dimensions[DIMENSION_' + match2 + ']';
|
||||
})
|
||||
.replace(// style.maxDimensions[CSS_WIDTH] => style.maxWidth
|
||||
/(style|layout)\.maxDimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.max' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||
})
|
||||
.replace(// style.minDimensions[CSS_WIDTH] => style.minWidth
|
||||
/(style|layout)\.minDimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.min' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||
})
|
||||
.replace(// style.margin[CSS_TOP] = 12.3 => style.margin[Spacing.TOP].set(12.3)
|
||||
/style\.(margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT|START|END)\]\s+=\s+(-?[\.\d]+)/g,
|
||||
function(str, match1, match2, match3) {
|
||||
var propertyCap = match1.charAt(0).toUpperCase() + match1.slice(1);
|
||||
return 'set' + propertyCap + '(Spacing.' + match2 + ', ' + match3 + ')';
|
||||
})
|
||||
.replace(// style.margin[CSS_TOP] => style.margin[Spacing.TOP]
|
||||
/style\.(margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT|START|END)\]/g,
|
||||
function(str, match1, match2) {
|
||||
return 'style.' + match1 + '.get(Spacing.' + match2 + ')';
|
||||
})
|
||||
.replace(/get_child\(.*context\,\s([^\)]+)\)/g, 'getChildAt($1)')
|
||||
.replace(/init_css_node_children/g, 'addChildren')
|
||||
.replace(/css_node_t(\s)\*/g, 'TestCSSNode$1')
|
||||
.replace(/\->/g, '.')
|
||||
.replace(/(\d+\.\d+)/g, '$1f')
|
||||
.replace(// style.flex_direction => style.flexDirection
|
||||
/style\.([^_\[\]\s]+)_(\w)(\w+)/g,
|
||||
function(str, match1, match2, match3) {
|
||||
return 'style.' + match1 + match2.toUpperCase() + match3;
|
||||
})
|
||||
.replace(/(\w+)\.measure\s+=\s+.+/, '$1.setMeasureFunction(sTestMeasureFunction);')
|
||||
|
||||
// additional case conversions
|
||||
|
||||
.replace(/(CSSWrap|CSSFlexDirection)\.([_A-Z]+)/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.' + constantToPascalCase(match2);
|
||||
});
|
||||
}
|
||||
|
||||
function indent(code) {
|
||||
return code
|
||||
.split('\n')
|
||||
.map(function(line) { return ' ' + line; })
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function constantToPascalCase(str) {
|
||||
return str[0] + str.substr(1)
|
||||
.toLowerCase()
|
||||
.replace(/_(.)/g,
|
||||
function(_, m) { return m.toUpperCase(); });
|
||||
}
|
||||
|
||||
var CSharpTranspiler = {
|
||||
transpileLayoutEngine: function(code) {
|
||||
return indent(
|
||||
__transpileToCSharpCommon(code)
|
||||
.replace(/function\s+layoutNode.*/, '')
|
||||
.replace('node.style.measure', 'node.measure')
|
||||
.replace(/\.children\.length/g, '.getChildCount()')
|
||||
.replace(/node.children\[i\]/g, 'node.getChildAt(i)')
|
||||
.replace(/node.children\[ii\]/g, 'node.getChildAt(ii)')
|
||||
.replace(/fmaxf/g, 'Math.Max')
|
||||
.replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages
|
||||
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
||||
.replace(/ === /g, ' == ')
|
||||
.replace(/ !== /g, ' != ')
|
||||
.replace(/\n {2}/g, '\n')
|
||||
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
|
||||
.replace(/css_node_t\*/g, 'CSSNode'));
|
||||
},
|
||||
|
||||
transpileCConstDefs: function(cConstDefs) {
|
||||
return indent(
|
||||
cConstDefs
|
||||
.replace(/#define\s+(\w+)\s+(\"[^\"]+\")/g, 'public static readonly string $1 = $2;')
|
||||
.replace(/#define\s+(\w+)\s+(.+)/g, 'public static readonly float $1 = $2f;'));
|
||||
},
|
||||
|
||||
transpileCTestsArray: function(allTestsInC) {
|
||||
var allTestsInCSharp = [];
|
||||
for (var i = 0; i < allTestsInC.length; i++) {
|
||||
allTestsInCSharp[i] =
|
||||
' [Test]\n' +
|
||||
' public void TestCase' + i + '()\n' +
|
||||
__transpileSingleTestToCSharp(allTestsInC[i]);
|
||||
}
|
||||
return allTestsInCSharp.join('\n\n');
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = CSharpTranspiler;
|
||||
}
|
@@ -10,84 +10,85 @@
|
||||
function __transpileToJavaCommon(code) {
|
||||
return code
|
||||
.replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED')
|
||||
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
|
||||
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
|
||||
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
|
||||
.replace(/css_flex_direction_t/g, 'CSSFlexDirection')
|
||||
.replace(/css_direction_t/g, 'CSSDirection')
|
||||
.replace(/CSS_DIRECTION_/g, 'CSSDirection.')
|
||||
.replace(/CSS_FLEX_DIRECTION_/g, 'CSSFlexDirection.')
|
||||
.replace(/css_align_t/g, 'CSSAlign')
|
||||
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
|
||||
.replace(/CSS_WRAP/g, 'CSSWrap.WRAP')
|
||||
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
|
||||
.replace(/css_justify_t/g, 'CSSJustify')
|
||||
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
|
||||
.replace(/css_dim_t/g, 'MeasureOutput')
|
||||
.replace(/bool/g, 'boolean')
|
||||
.replace(/^(\s+)([^\s]+)\s+\+=/gm, '$1$2 = $2 +') // Expand +=
|
||||
.replace(/leading\[([^\]]+)\]/g, 'getLeading($1)')
|
||||
.replace(/trailing\[([^\]]+)\]/g, 'getTrailing($1)')
|
||||
.replace(/pos\[([^\]]+)\]/g, 'getPos($1)')
|
||||
.replace(/dim\[([^\]]+)\]/g, 'getDim($1)')
|
||||
.replace(/isUndefined/g, 'CSSConstants.isUndefined')
|
||||
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||
|
||||
// Since Java doesn't store its attributes in arrays, we need to use setters/getters to access
|
||||
// the appropriate layout/style fields
|
||||
.replace(
|
||||
/(\w+)\.layout\[((?:getLeading|getPos)\([^\)]+\))\]\s+=\s+([^;]+);/gm,
|
||||
'setLayoutPosition($1, $2, $3);')
|
||||
.replace(
|
||||
/(\w+)\.layout\[((?:getTrailing|getPos)\([^\)]+\))\]\s+=\s+([^;]+);/gm,
|
||||
'setLayoutPosition($1, $2, $3);')
|
||||
.replace(
|
||||
/(\w+)\.layout\.direction\s+=\s+([^;]+);/gm,
|
||||
'setLayoutDirection($1, $2);')
|
||||
.replace(/(\w+)\.layout\[((?:getLeading|getPos)\([^\]]+\))\]/g, 'getLayoutPosition($1, $2)')
|
||||
.replace(/(\w+)\.layout\[((?:getTrailing|getPos)\([^\]]+\))\]/g, 'getLayoutPosition($1, $2)')
|
||||
.replace(
|
||||
/(\w+)\.layout\[(getDim\([^\)]+\))\]\s+=\s+([^;]+);/gm,
|
||||
'setLayoutDimension($1, $2, $3);')
|
||||
.replace(/(\w+)\.layout\[(getDim\([^\]]+\))\]/g, 'getLayoutDimension($1, $2)')
|
||||
.replace(/(\w+)\.style\[((?:getLeading|getPos)\([^\]]+\))\]/g, 'getStylePosition($1, $2)')
|
||||
.replace(/(\w+)\.style\[(getDim\([^\]]+\))\]/g, 'getStyleDimension($1, $2)');
|
||||
.replace(/style\[dim/g, 'style.dimensions[dim')
|
||||
.replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]')
|
||||
.replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]')
|
||||
.replace(/layout\[dim/g, 'layout.dimensions[dim')
|
||||
.replace(/layout\[pos/g, 'layout.position[pos')
|
||||
.replace(/layout\[leading/g, 'layout.position[leading')
|
||||
.replace(/layout\[trailing/g, 'layout.position[trailing')
|
||||
.replace(/getPositionType\((.+?)\)/g, '$1.style.positionType')
|
||||
.replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent')
|
||||
.replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent')
|
||||
.replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)')
|
||||
.replace(/isDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] >= 0.0\)')
|
||||
.replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)')
|
||||
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]')
|
||||
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)')
|
||||
.replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)')
|
||||
.replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)')
|
||||
.replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)')
|
||||
.replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
|
||||
.replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)')
|
||||
.replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)')
|
||||
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
|
||||
.replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||
.replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||
.replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||
.replace(/getTrailingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||
.replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||
.replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||
.replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)')
|
||||
.replace(/isUndefined\((.+?)\)/g, 'Float.isNaN\($1\)')
|
||||
.replace(/\/\*\(c\)!([^*]+)\*\//g, '')
|
||||
.replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1');
|
||||
}
|
||||
|
||||
function __transpileSingleTestToJava(code) {
|
||||
return __transpileToJavaCommon(code)
|
||||
.replace(/CSS_DIRECTION_/g, 'CSSDirection.')
|
||||
.replace(/CSS_FLEX_DIRECTION_/g, 'CSSFlexDirection.')
|
||||
.replace(/CSS_WRAP/g, 'CSSWrap.WRAP')
|
||||
.replace(/new_test_css_node/g, 'new TestCSSNode')
|
||||
.replace( // style.dimensions[CSS_WIDTH] => style.width
|
||||
.replace(// style.position[CSS_TOP] => style.position[CSSLayout.POSITION_TOP]
|
||||
/(style|layout)\.position\[CSS_(LEFT|TOP|RIGHT|BOTTOM)\]/g,
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.position[POSITION_' + match2 + ']';
|
||||
})
|
||||
.replace(// style.dimensions[CSS_WIDTH] => style.dimensions[CSSLayout.DIMENSION_WIDTH]
|
||||
/(style|layout)\.dimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||
function (str, match1, match2) {
|
||||
return match1 + '.' + match2.toLowerCase();
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.dimensions[DIMENSION_' + match2 + ']';
|
||||
})
|
||||
.replace( // style.maxDimensions[CSS_WIDTH] => style.maxWidth
|
||||
.replace(// style.maxDimensions[CSS_WIDTH] => style.maxWidth
|
||||
/(style|layout)\.maxDimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||
function (str, match1, match2) {
|
||||
return match1 + '.max' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.max' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||
})
|
||||
.replace( // style.minDimensions[CSS_WIDTH] => style.minWidth
|
||||
.replace(// style.minDimensions[CSS_WIDTH] => style.minWidth
|
||||
/(style|layout)\.minDimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||
function (str, match1, match2) {
|
||||
return match1 + '.min' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||
function(str, match1, match2) {
|
||||
return match1 + '.min' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||
})
|
||||
.replace( // layout.position[CSS_TOP] => layout.y
|
||||
/layout\.position\[CSS_(TOP|LEFT)\]/g,
|
||||
function (str, match1) {
|
||||
return 'layout.' + (match1 === 'TOP' ? 'top' : 'left');
|
||||
})
|
||||
.replace( // style.position[CSS_TOP] => style.positionTop
|
||||
/style\.(position)\[CSS_(TOP|BOTTOM|LEFT|RIGHT)\]/g,
|
||||
function (str, match1, match2) {
|
||||
return 'style.' + match1 + match2[0] + match2.substring(1).toLowerCase();
|
||||
})
|
||||
.replace( // style.margin[CSS_TOP] = 12.3 => style.margin[Spacing.TOP].set(12.3)
|
||||
.replace(// style.margin[CSS_TOP] = 12.3 => style.margin[Spacing.TOP].set(12.3)
|
||||
/style\.(margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT|START|END)\]\s+=\s+(-?[\.\d]+)/g,
|
||||
function (str, match1, match2, match3) {
|
||||
function(str, match1, match2, match3) {
|
||||
var propertyCap = match1.charAt(0).toUpperCase() + match1.slice(1);
|
||||
return 'set' + propertyCap + '(Spacing.' + match2 + ', ' + match3 + ')';
|
||||
})
|
||||
.replace( // style.margin[CSS_TOP] => style.margin[Spacing.TOP]
|
||||
.replace(// style.margin[CSS_TOP] => style.margin[Spacing.TOP]
|
||||
/style\.(margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT|START|END)\]/g,
|
||||
function (str, match1, match2) {
|
||||
function(str, match1, match2) {
|
||||
return 'style.' + match1 + '.get(Spacing.' + match2 + ')';
|
||||
})
|
||||
.replace(/get_child\(.*context\,\s([^\)]+)\)/g, 'getChildAt($1)')
|
||||
@@ -95,10 +96,10 @@ function __transpileSingleTestToJava(code) {
|
||||
.replace(/css_node_t(\s)\*/g, 'TestCSSNode$1')
|
||||
.replace(/\->/g, '.')
|
||||
.replace(/(\d+\.\d+)/g, '$1f')
|
||||
.replace( // style.flex_direction => style.flexDirection
|
||||
.replace(// style.flex_direction => style.flexDirection
|
||||
/style\.([^_\[\]\s]+)_(\w)(\w+)/g,
|
||||
function (str, match1, match2, match3) {
|
||||
return 'style.' + match1 + match2.toUpperCase() + match3;
|
||||
function(str, match1, match2, match3) {
|
||||
return 'style.' + match1 + match2.toUpperCase() + match3;
|
||||
})
|
||||
.replace(/(\w+)\.measure\s+=\s+.+/, '$1.setMeasureFunction(sTestMeasureFunction);');
|
||||
}
|
||||
|
@@ -23,13 +23,13 @@ var layoutTestUtils = (function() {
|
||||
var testMeasurePrecision = 1.0;
|
||||
|
||||
if (typeof jasmine !== 'undefined') {
|
||||
jasmine.matchersUtil.buildFailureMessage = function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0),
|
||||
matcherName = args[0],
|
||||
isNot = args[1],
|
||||
actual = args[2],
|
||||
expected = args.slice(3),
|
||||
englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
|
||||
jasmine.matchersUtil.buildFailureMessage = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var matcherName = args[0];
|
||||
var isNot = args[1];
|
||||
var actual = args[2];
|
||||
var expected = args.slice(3);
|
||||
var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
|
||||
|
||||
var pp = function(node) {
|
||||
return jasmine.pp(node)
|
||||
@@ -278,8 +278,7 @@ var layoutTestUtils = (function() {
|
||||
var val = obj[key];
|
||||
if (typeof val === 'number') {
|
||||
obj[key] = Math.floor((val * testMeasurePrecision) + 0.5) / testMeasurePrecision;
|
||||
}
|
||||
else if (typeof val === 'object') {
|
||||
} else if (typeof val === 'object') {
|
||||
inplaceRoundNumbersInObject(val);
|
||||
}
|
||||
}
|
||||
@@ -386,7 +385,7 @@ var layoutTestUtils = (function() {
|
||||
document.body.appendChild(iframeText);
|
||||
|
||||
var body = iframeText.contentDocument.body;
|
||||
if (width === undefined || width !== width) {
|
||||
if (width === undefined || isNaN(width)) {
|
||||
width = Infinity;
|
||||
}
|
||||
|
||||
@@ -490,7 +489,7 @@ var layoutTestUtils = (function() {
|
||||
reduceTest: reduceTest,
|
||||
text: function(text) {
|
||||
var fn = function(width) {
|
||||
if (width === undefined || width !== width) {
|
||||
if (width === undefined || isNaN(width)) {
|
||||
width = Infinity;
|
||||
}
|
||||
|
||||
|
474
src/Layout.c
474
src/Layout.c
@@ -376,18 +376,6 @@ static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis
|
||||
return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis);
|
||||
}
|
||||
|
||||
static css_position_type_t getPositionType(css_node_t *node) {
|
||||
return node->style.position_type;
|
||||
}
|
||||
|
||||
static css_justify_t getJustifyContent(css_node_t *node) {
|
||||
return node->style.justify_content;
|
||||
}
|
||||
|
||||
static css_align_t getAlignContent(css_node_t *node) {
|
||||
return node->style.align_content;
|
||||
}
|
||||
|
||||
static css_align_t getAlignItem(css_node_t *node, css_node_t *child) {
|
||||
if (child->style.align_self != CSS_ALIGN_AUTO) {
|
||||
return child->style.align_self;
|
||||
@@ -435,7 +423,7 @@ static float getFlex(css_node_t *node) {
|
||||
|
||||
static bool isFlex(css_node_t *node) {
|
||||
return (
|
||||
getPositionType(node) == CSS_POSITION_RELATIVE &&
|
||||
node->style.position_type == CSS_POSITION_RELATIVE &&
|
||||
getFlex(node) > 0
|
||||
);
|
||||
}
|
||||
@@ -452,7 +440,7 @@ static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
|
||||
|
||||
static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) {
|
||||
float value = node->style.dimensions[dim[axis]];
|
||||
return !isUndefined(value) && value > 0.0;
|
||||
return !isUndefined(value) && value >= 0.0;
|
||||
}
|
||||
|
||||
static bool isPosDefined(css_node_t *node, css_position_t position) {
|
||||
@@ -553,23 +541,29 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) +
|
||||
getRelativePosition(node, crossAxis);
|
||||
|
||||
// Inline immutable values from the target node to avoid excessive method
|
||||
// invocations during the layout calculation.
|
||||
int childCount = node->children_count;
|
||||
float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
|
||||
if (isMeasureDefined(node)) {
|
||||
bool isResolvedRowDimDefined = !isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]);
|
||||
|
||||
float width = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
width = node->style.dimensions[CSS_WIDTH];
|
||||
} else if (!isUndefined(node->layout.dimensions[dim[resolvedRowAxis]])) {
|
||||
} else if (isResolvedRowDimDefined) {
|
||||
width = node->layout.dimensions[dim[resolvedRowAxis]];
|
||||
} else {
|
||||
width = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis);
|
||||
}
|
||||
width -= getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
width -= paddingAndBorderAxisResolvedRow;
|
||||
|
||||
// We only need to give a dimension for the text if we haven't got any
|
||||
// for it computed yet. It can either be from the style attribute or because
|
||||
// the element is flexible.
|
||||
bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) &&
|
||||
isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]);
|
||||
bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
|
||||
bool isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&
|
||||
isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
|
||||
|
||||
@@ -582,66 +576,42 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
if (isColumnUndefined) {
|
||||
node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
|
||||
}
|
||||
}
|
||||
if (node->children_count == 0) {
|
||||
if (childCount == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool isNodeFlexWrap = isFlexWrap(node);
|
||||
|
||||
css_justify_t justifyContent = node->style.justify_content;
|
||||
|
||||
float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);
|
||||
float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);
|
||||
float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);
|
||||
float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);
|
||||
|
||||
bool isMainDimDefined = !isUndefined(node->layout.dimensions[dim[mainAxis]]);
|
||||
bool isCrossDimDefined = !isUndefined(node->layout.dimensions[dim[crossAxis]]);
|
||||
bool isMainRowDirection = isRowDirection(mainAxis);
|
||||
|
||||
int i;
|
||||
int ii;
|
||||
css_node_t* child;
|
||||
css_flex_direction_t axis;
|
||||
|
||||
// Pre-fill some dimensions straight from the parent
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||
// we call the recursive layout pass
|
||||
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
|
||||
getPositionType(child) == CSS_POSITION_RELATIVE &&
|
||||
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
|
||||
!isDimDefined(child, crossAxis)) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
} else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
css_node_t* firstAbsoluteChild = NULL;
|
||||
css_node_t* currentAbsoluteChild = NULL;
|
||||
|
||||
float definedMainDim = CSS_UNDEFINED;
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
definedMainDim = node->layout.dimensions[dim[mainAxis]] -
|
||||
getPaddingAndBorderAxis(node, mainAxis);
|
||||
if (isMainDimDefined) {
|
||||
definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
|
||||
}
|
||||
|
||||
// We want to execute the next two loops one per line with flex-wrap
|
||||
@@ -653,7 +623,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
float linesCrossDim = 0;
|
||||
float linesMainDim = 0;
|
||||
int linesCount = 0;
|
||||
while (endLine < node->children_count) {
|
||||
while (endLine < childCount) {
|
||||
// <Loop A> Layout non flexible children and count children by type
|
||||
|
||||
// mainContentDim is accumulation of the dimensions and margin of all the
|
||||
@@ -668,16 +638,99 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
float totalFlexible = 0;
|
||||
int nonFlexibleChildrenCount = 0;
|
||||
|
||||
// Use the line loop to position children in the main axis for as long
|
||||
// as they are using a simple stacking behaviour. Children that are
|
||||
// immediately stacked in the initial loop will not be touched again
|
||||
// in <Loop C>.
|
||||
bool isSimpleStackMain =
|
||||
(isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) ||
|
||||
(!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER);
|
||||
int firstComplexMain = (isSimpleStackMain ? childCount : startLine);
|
||||
|
||||
// Use the initial line loop to position children in the cross axis for
|
||||
// as long as they are relatively positioned with alignment STRETCH or
|
||||
// FLEX_START. Children that are immediately stacked in the initial loop
|
||||
// will not be touched again in <Loop D>.
|
||||
bool isSimpleStackCross = true;
|
||||
int firstComplexCross = childCount;
|
||||
|
||||
css_node_t* firstFlexChild = NULL;
|
||||
css_node_t* currentFlexChild = NULL;
|
||||
|
||||
float mainDim = leadingPaddingAndBorderMain;
|
||||
float crossDim = 0;
|
||||
|
||||
float maxWidth;
|
||||
for (i = startLine; i < node->children_count; ++i) {
|
||||
for (i = startLine; i < childCount; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
child->line_index = linesCount;
|
||||
|
||||
child->next_absolute_child = NULL;
|
||||
child->next_flex_child = NULL;
|
||||
|
||||
css_align_t alignItem = getAlignItem(node, child);
|
||||
|
||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||
// we call the recursive layout pass
|
||||
if (alignItem == CSS_ALIGN_STRETCH &&
|
||||
child->style.position_type == CSS_POSITION_RELATIVE &&
|
||||
isCrossDimDefined &&
|
||||
!isDimDefined(child, crossAxis)) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
|
||||
paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
} else if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
|
||||
// Store a private linked list of absolutely positioned children
|
||||
// so that we can efficiently traverse them later.
|
||||
if (firstAbsoluteChild == NULL) {
|
||||
firstAbsoluteChild = child;
|
||||
}
|
||||
if (currentAbsoluteChild != NULL) {
|
||||
currentAbsoluteChild->next_absolute_child = child;
|
||||
}
|
||||
currentAbsoluteChild = child;
|
||||
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float nextContentDim = 0;
|
||||
|
||||
// It only makes sense to consider a child flexible if we have a computed
|
||||
// dimension for the node->
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) {
|
||||
if (isMainDimDefined && isFlex(child)) {
|
||||
flexibleChildrenCount++;
|
||||
totalFlexible += getFlex(child);
|
||||
totalFlexible += child->style.flex;
|
||||
|
||||
// Store a private linked list of flexible children so that we can
|
||||
// efficiently traverse them later.
|
||||
if (firstFlexChild == NULL) {
|
||||
firstFlexChild = child;
|
||||
}
|
||||
if (currentFlexChild != NULL) {
|
||||
currentFlexChild->next_flex_child = child;
|
||||
}
|
||||
currentFlexChild = child;
|
||||
|
||||
// Even if we don't know its exact size yet, we already know the padding,
|
||||
// border and margin. We'll use this partial information, which represents
|
||||
@@ -688,14 +741,14 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
} else {
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (!isRowDirection(mainAxis)) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
|
||||
if (!isMainRowDirection) {
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
} else {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +759,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// Absolute positioned elements do not take part of the layout, so we
|
||||
// don't use them to compute mainContentDim
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type == CSS_POSITION_RELATIVE) {
|
||||
nonFlexibleChildrenCount++;
|
||||
// At this point we know the final size and margin of the element.
|
||||
nextContentDim = getDimWithMargin(child, mainAxis);
|
||||
@@ -714,8 +767,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
}
|
||||
|
||||
// The element we are about to add would make us go to the next line
|
||||
if (isFlexWrap(node) &&
|
||||
!isUndefined(node->layout.dimensions[dim[mainAxis]]) &&
|
||||
if (isNodeFlexWrap &&
|
||||
isMainDimDefined &&
|
||||
mainContentDim + nextContentDim > definedMainDim &&
|
||||
// If there's only one element, then it's bigger than the content
|
||||
// and needs its own line
|
||||
@@ -724,6 +777,44 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
alreadyComputedNextLayout = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable simple stacking in the main axis for the current line as
|
||||
// we found a non-trivial child-> The remaining children will be laid out
|
||||
// in <Loop C>.
|
||||
if (isSimpleStackMain &&
|
||||
(child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) {
|
||||
isSimpleStackMain = false;
|
||||
firstComplexMain = i;
|
||||
}
|
||||
|
||||
// Disable simple stacking in the cross axis for the current line as
|
||||
// we found a non-trivial child-> The remaining children will be laid out
|
||||
// in <Loop D>.
|
||||
if (isSimpleStackCross &&
|
||||
(child->style.position_type != CSS_POSITION_RELATIVE ||
|
||||
(alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) ||
|
||||
isUndefined(child->layout.dimensions[dim[crossAxis]]))) {
|
||||
isSimpleStackCross = false;
|
||||
firstComplexCross = i;
|
||||
}
|
||||
|
||||
if (isSimpleStackMain) {
|
||||
child->layout.position[pos[mainAxis]] += mainDim;
|
||||
if (isMainDimDefined) {
|
||||
setTrailingPosition(node, child, mainAxis);
|
||||
}
|
||||
|
||||
mainDim += getDimWithMargin(child, mainAxis);
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
}
|
||||
|
||||
if (isSimpleStackCross) {
|
||||
child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
|
||||
if (isCrossDimDefined) {
|
||||
setTrailingPosition(node, child, crossAxis);
|
||||
}
|
||||
}
|
||||
|
||||
alreadyComputedNextLayout = 0;
|
||||
mainContentDim += nextContentDim;
|
||||
endLine = i + 1;
|
||||
@@ -739,7 +830,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// The remaining available space that needs to be allocated
|
||||
float remainingMainDim = 0;
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
if (isMainDimDefined) {
|
||||
remainingMainDim = definedMainDim - mainContentDim;
|
||||
} else {
|
||||
remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;
|
||||
@@ -752,21 +843,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
float baseMainDim;
|
||||
float boundMainDim;
|
||||
|
||||
// Iterate over every child in the axis. If the flex share of remaining
|
||||
// space doesn't meet min/max bounds, remove this child from flex
|
||||
// calculations.
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (isFlex(child)) {
|
||||
baseMainDim = flexibleMainDim * getFlex(child) +
|
||||
getPaddingAndBorderAxis(child, mainAxis);
|
||||
boundMainDim = boundAxis(child, mainAxis, baseMainDim);
|
||||
// If the flex share of remaining space doesn't meet min/max bounds,
|
||||
// remove this child from flex calculations.
|
||||
currentFlexChild = firstFlexChild;
|
||||
while (currentFlexChild != NULL) {
|
||||
baseMainDim = flexibleMainDim * currentFlexChild->style.flex +
|
||||
getPaddingAndBorderAxis(currentFlexChild, mainAxis);
|
||||
boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
|
||||
|
||||
if (baseMainDim != boundMainDim) {
|
||||
remainingMainDim -= boundMainDim;
|
||||
totalFlexible -= getFlex(child);
|
||||
}
|
||||
if (baseMainDim != boundMainDim) {
|
||||
remainingMainDim -= boundMainDim;
|
||||
totalFlexible -= currentFlexChild->style.flex;
|
||||
}
|
||||
|
||||
currentFlexChild = currentFlexChild->next_flex_child;
|
||||
}
|
||||
flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
|
||||
@@ -775,37 +865,37 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
if (flexibleMainDim < 0) {
|
||||
flexibleMainDim = 0;
|
||||
}
|
||||
// We iterate over the full array and only apply the action on flexible
|
||||
// children. This is faster than actually allocating a new array that
|
||||
// contains only flexible children.
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (isFlex(child)) {
|
||||
// At this point we know the final size of the element in the main
|
||||
// dimension
|
||||
child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis,
|
||||
flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis)
|
||||
);
|
||||
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
} else if (!isRowDirection(mainAxis)) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
getPaddingAndBorderAxis(node, resolvedRowAxis);
|
||||
}
|
||||
currentFlexChild = firstFlexChild;
|
||||
while (currentFlexChild != NULL) {
|
||||
// At this point we know the final size of the element in the main
|
||||
// dimension
|
||||
currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
|
||||
flexibleMainDim * currentFlexChild->style.flex +
|
||||
getPaddingAndBorderAxis(currentFlexChild, mainAxis)
|
||||
);
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(child, maxWidth, direction);
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, resolvedRowAxis)) {
|
||||
maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
} else if (!isMainRowDirection) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, resolvedRowAxis) -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(currentFlexChild, maxWidth, direction);
|
||||
|
||||
child = currentFlexChild;
|
||||
currentFlexChild = currentFlexChild->next_flex_child;
|
||||
child->next_flex_child = NULL;
|
||||
}
|
||||
|
||||
// We use justifyContent to figure out how to allocate the remaining
|
||||
// space available
|
||||
} else {
|
||||
css_justify_t justifyContent = getJustifyContent(node);
|
||||
} else if (justifyContent != CSS_JUSTIFY_FLEX_START) {
|
||||
if (justifyContent == CSS_JUSTIFY_CENTER) {
|
||||
leadingMainDim = remainingMainDim / 2;
|
||||
} else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
|
||||
@@ -832,15 +922,12 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
// find their position. In order to do that, we accumulate data in
|
||||
// variables that are also useful to compute the total dimensions of the
|
||||
// container!
|
||||
float crossDim = 0;
|
||||
float mainDim = leadingMainDim +
|
||||
getLeadingPaddingAndBorder(node, mainAxis);
|
||||
mainDim += leadingMainDim;
|
||||
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
for (i = firstComplexMain; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
child->line_index = linesCount;
|
||||
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||
if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
|
||||
isPosDefined(child, leading[mainAxis])) {
|
||||
// In case the child is position absolute and has left/top being
|
||||
// defined, we override the position to whatever the user said
|
||||
@@ -854,40 +941,40 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
child->layout.position[pos[mainAxis]] += mainDim;
|
||||
|
||||
// Define the trailing position accordingly.
|
||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
if (isMainDimDefined) {
|
||||
setTrailingPosition(node, child, mainAxis);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we placed the element, we need to update the variables
|
||||
// We only need to do that for relative elements. Absolute elements
|
||||
// do not take part in that phase.
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
// The main dimension is the sum of all the elements dimension plus
|
||||
// the spacing.
|
||||
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
|
||||
// The cross dimension is the max of the elements dimension since there
|
||||
// can only be one element in that cross dimension.
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
// Now that we placed the element, we need to update the variables
|
||||
// We only need to do that for relative elements. Absolute elements
|
||||
// do not take part in that phase.
|
||||
if (child->style.position_type == CSS_POSITION_RELATIVE) {
|
||||
// The main dimension is the sum of all the elements dimension plus
|
||||
// the spacing.
|
||||
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
|
||||
// The cross dimension is the max of the elements dimension since there
|
||||
// can only be one element in that cross dimension.
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float containerCrossAxis = node->layout.dimensions[dim[crossAxis]];
|
||||
if (isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (!isCrossDimDefined) {
|
||||
containerCrossAxis = fmaxf(
|
||||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)),
|
||||
getPaddingAndBorderAxis(node, crossAxis)
|
||||
boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
|
||||
paddingAndBorderAxisCross
|
||||
);
|
||||
}
|
||||
|
||||
// <Loop D> Position elements in the cross axis
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
for (i = firstComplexCross; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||
if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
|
||||
isPosDefined(child, leading[crossAxis])) {
|
||||
// In case the child is absolutely positionned and has a
|
||||
// top/left/bottom/right being set, we override all the previously
|
||||
@@ -897,20 +984,22 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
getLeadingMargin(child, crossAxis);
|
||||
|
||||
} else {
|
||||
float leadingCrossDim = getLeadingPaddingAndBorder(node, crossAxis);
|
||||
float leadingCrossDim = leadingPaddingAndBorderCross;
|
||||
|
||||
// For a relative children, we're either using alignItems (parent) or
|
||||
// alignSelf (child) in order to determine the position in the cross axis
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type == CSS_POSITION_RELATIVE) {
|
||||
/*eslint-disable */
|
||||
// This variable is intentionally re-defined as the code is transpiled to a block scope language
|
||||
css_align_t alignItem = getAlignItem(node, child);
|
||||
/*eslint-enable */
|
||||
if (alignItem == CSS_ALIGN_STRETCH) {
|
||||
// You can only stretch if the dimension has not already been set
|
||||
// previously.
|
||||
if (!isDimDefined(child, crossAxis)) {
|
||||
if (isUndefined(child->layout.dimensions[dim[crossAxis]])) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
boundAxis(child, crossAxis, containerCrossAxis -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getMarginAxis(child, crossAxis)),
|
||||
paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
@@ -919,8 +1008,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
// The remaining space between the parent dimensions+padding and child
|
||||
// dimensions+margin.
|
||||
float remainingCrossDim = containerCrossAxis -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getDimWithMargin(child, crossAxis);
|
||||
paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis);
|
||||
|
||||
if (alignItem == CSS_ALIGN_CENTER) {
|
||||
leadingCrossDim += remainingCrossDim / 2;
|
||||
@@ -934,7 +1022,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
|
||||
|
||||
// Define the trailing position accordingly.
|
||||
if (!isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (isCrossDimDefined) {
|
||||
setTrailingPosition(node, child, crossAxis);
|
||||
}
|
||||
}
|
||||
@@ -959,16 +1047,15 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
|
||||
// section 9.4
|
||||
//
|
||||
if (linesCount > 1 &&
|
||||
!isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (linesCount > 1 && isCrossDimDefined) {
|
||||
float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] -
|
||||
getPaddingAndBorderAxis(node, crossAxis);
|
||||
paddingAndBorderAxisCross;
|
||||
float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
|
||||
|
||||
float crossDimLead = 0;
|
||||
float currentLead = getLeadingPaddingAndBorder(node, crossAxis);
|
||||
float currentLead = leadingPaddingAndBorderCross;
|
||||
|
||||
css_align_t alignContent = getAlignContent(node);
|
||||
css_align_t alignContent = node->style.align_content;
|
||||
if (alignContent == CSS_ALIGN_FLEX_END) {
|
||||
currentLead += remainingAlignContentDim;
|
||||
} else if (alignContent == CSS_ALIGN_CENTER) {
|
||||
@@ -985,9 +1072,9 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// compute the line's height and find the endIndex
|
||||
float lineHeight = 0;
|
||||
for (ii = startIndex; ii < node->children_count; ++ii) {
|
||||
for (ii = startIndex; ii < childCount; ++ii) {
|
||||
child = node->get_child(node->context, ii);
|
||||
if (getPositionType(child) != CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type != CSS_POSITION_RELATIVE) {
|
||||
continue;
|
||||
}
|
||||
if (child->line_index != i) {
|
||||
@@ -1005,7 +1092,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
for (ii = startIndex; ii < endIndex; ++ii) {
|
||||
child = node->get_child(node->context, ii);
|
||||
if (getPositionType(child) != CSS_POSITION_RELATIVE) {
|
||||
if (child->style.position_type != CSS_POSITION_RELATIVE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1033,33 +1120,39 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
|
||||
// If the user didn't specify a width or height, and it has not been set
|
||||
// by the container, then we set it via the children.
|
||||
if (isUndefined(node->layout.dimensions[dim[mainAxis]])) {
|
||||
if (!isMainDimDefined) {
|
||||
node->layout.dimensions[dim[mainAxis]] = fmaxf(
|
||||
// We're missing the last padding at this point to get the final
|
||||
// dimension
|
||||
boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),
|
||||
// We can never assign a width smaller than the padding and borders
|
||||
getPaddingAndBorderAxis(node, mainAxis)
|
||||
paddingAndBorderAxisMain
|
||||
);
|
||||
|
||||
needsMainTrailingPos = true;
|
||||
if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||
mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||
needsMainTrailingPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUndefined(node->layout.dimensions[dim[crossAxis]])) {
|
||||
if (!isCrossDimDefined) {
|
||||
node->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)),
|
||||
getPaddingAndBorderAxis(node, crossAxis)
|
||||
boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
|
||||
paddingAndBorderAxisCross
|
||||
);
|
||||
|
||||
needsCrossTrailingPos = true;
|
||||
if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||
crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||
needsCrossTrailingPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
// <Loop F> Set trailing position if necessary
|
||||
if (needsMainTrailingPos || needsCrossTrailingPos) {
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
for (i = 0; i < childCount; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
|
||||
if (needsMainTrailingPos) {
|
||||
@@ -1073,40 +1166,41 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
|
||||
}
|
||||
|
||||
// <Loop G> Calculate dimensions for absolutely positioned elements
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])
|
||||
),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
currentAbsoluteChild = firstAbsoluteChild;
|
||||
while (currentAbsoluteChild != NULL) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for
|
||||
// the axis are defined (either both left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(currentAbsoluteChild, axis) &&
|
||||
isPosDefined(currentAbsoluteChild, leading[axis]) &&
|
||||
isPosDefined(currentAbsoluteChild, trailing[axis])) {
|
||||
currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf(
|
||||
boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] -
|
||||
getBorderAxis(node, axis) -
|
||||
getMarginAxis(currentAbsoluteChild, axis) -
|
||||
getPosition(currentAbsoluteChild, leading[axis]) -
|
||||
getPosition(currentAbsoluteChild, trailing[axis])
|
||||
),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(currentAbsoluteChild, axis)
|
||||
);
|
||||
}
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (isPosDefined(child, trailing[axis]) &&
|
||||
!isPosDefined(child, leading[axis])) {
|
||||
child->layout.position[leading[axis]] =
|
||||
node->layout.dimensions[dim[axis]] -
|
||||
child->layout.dimensions[dim[axis]] -
|
||||
getPosition(child, trailing[axis]);
|
||||
}
|
||||
|
||||
if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
|
||||
!isPosDefined(currentAbsoluteChild, leading[axis])) {
|
||||
currentAbsoluteChild->layout.position[leading[axis]] =
|
||||
node->layout.dimensions[dim[axis]] -
|
||||
currentAbsoluteChild->layout.dimensions[dim[axis]] -
|
||||
getPosition(currentAbsoluteChild, trailing[axis]);
|
||||
}
|
||||
}
|
||||
|
||||
child = currentAbsoluteChild;
|
||||
currentAbsoluteChild = currentAbsoluteChild->next_absolute_child;
|
||||
child->next_absolute_child = NULL;
|
||||
}
|
||||
/** END_GENERATED **/
|
||||
}
|
||||
|
@@ -129,18 +129,22 @@ typedef struct {
|
||||
float maxDimensions[2];
|
||||
} css_style_t;
|
||||
|
||||
typedef struct css_node {
|
||||
typedef struct css_node css_node_t;
|
||||
struct css_node {
|
||||
css_style_t style;
|
||||
css_layout_t layout;
|
||||
int children_count;
|
||||
int line_index;
|
||||
|
||||
css_node_t* next_absolute_child;
|
||||
css_node_t* next_flex_child;
|
||||
|
||||
css_dim_t (*measure)(void *context, float width);
|
||||
void (*print)(void *context);
|
||||
struct css_node* (*get_child)(void *context, int i);
|
||||
bool (*is_dirty)(void *context);
|
||||
void *context;
|
||||
} css_node_t;
|
||||
};
|
||||
|
||||
|
||||
// Lifecycle of nodes and children
|
||||
|
745
src/Layout.js
745
src/Layout.js
File diff suppressed because it is too large
Load Diff
@@ -98,13 +98,13 @@ describe('Random layout', function() {
|
||||
}
|
||||
|
||||
function checkRandomLayout(i, node) {
|
||||
it('should layout randomly #' + i + '.', function(node) {
|
||||
if (JSON.stringify(computeLayout(node)) !== JSON.stringify(computeDOMLayout(node))) {
|
||||
node = reduceTest(node);
|
||||
}
|
||||
it('should layout randomly #' + i + '.', function(node) {
|
||||
if (JSON.stringify(computeLayout(node)) !== JSON.stringify(computeDOMLayout(node))) {
|
||||
node = reduceTest(node);
|
||||
}
|
||||
|
||||
testRandomLayout(node, i);
|
||||
}.bind(this, node));
|
||||
testRandomLayout(node, i);
|
||||
}.bind(this, node));
|
||||
}
|
||||
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
|
@@ -7468,6 +7468,40 @@ int main()
|
||||
test("should layout node with correct start/end border in rtl", root_node, root_layout);
|
||||
}
|
||||
|
||||
{
|
||||
css_node_t *root_node = new_test_css_node();
|
||||
{
|
||||
css_node_t *node_0 = root_node;
|
||||
node_0->style.dimensions[CSS_WIDTH] = 200;
|
||||
init_css_node_children(node_0, 1);
|
||||
{
|
||||
css_node_t *node_1;
|
||||
node_1 = node_0->get_child(node_0->context, 0);
|
||||
node_1->style.dimensions[CSS_WIDTH] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
css_node_t *root_layout = new_test_css_node();
|
||||
{
|
||||
css_node_t *node_0 = root_layout;
|
||||
node_0->layout.position[CSS_TOP] = 0;
|
||||
node_0->layout.position[CSS_LEFT] = 0;
|
||||
node_0->layout.dimensions[CSS_WIDTH] = 200;
|
||||
node_0->layout.dimensions[CSS_HEIGHT] = 0;
|
||||
init_css_node_children(node_0, 1);
|
||||
{
|
||||
css_node_t *node_1;
|
||||
node_1 = node_0->get_child(node_0->context, 0);
|
||||
node_1->layout.position[CSS_TOP] = 0;
|
||||
node_1->layout.position[CSS_LEFT] = 0;
|
||||
node_1->layout.dimensions[CSS_WIDTH] = 0;
|
||||
node_1->layout.dimensions[CSS_HEIGHT] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
test("should layout node with a 0 width", root_node, root_layout);
|
||||
}
|
||||
|
||||
{
|
||||
css_node_t *root_node = new_test_css_node();
|
||||
{
|
||||
|
@@ -11,7 +11,6 @@
|
||||
var testLayout = layoutTestUtils.testLayout;
|
||||
var testLayoutAgainstDomOnly = layoutTestUtils.testLayoutAgainstDomOnly;
|
||||
var testFillNodes = layoutTestUtils.testFillNodes;
|
||||
var testExtractNodes = layoutTestUtils.testExtractNodes;
|
||||
var text = layoutTestUtils.text;
|
||||
var texts = layoutTestUtils.texts;
|
||||
var textSizes = layoutTestUtils.textSizes;
|
||||
@@ -2298,6 +2297,17 @@ describe('Layout', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should layout node with a 0 width', function() {
|
||||
testLayout(
|
||||
{style: {width: 200}, children: [
|
||||
{style: {width: 0}}
|
||||
]},
|
||||
{width: 200, height: 0, top: 0, left: 0, children: [
|
||||
{width: 0, height: 0, top: 0, left: 0}
|
||||
]}
|
||||
);
|
||||
});
|
||||
|
||||
xit('should stretch a nested child', function() {
|
||||
testLayout(
|
||||
{children: [
|
||||
|
13
src/csharp/.editorconfig
Normal file
13
src/csharp/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line=LF
|
||||
|
||||
[*.cs]
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
[*.js]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
6
src/csharp/.gitignore
vendored
Normal file
6
src/csharp/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
/.vs/
|
||||
*.user
|
||||
*.nupkg
|
53
src/csharp/Facebook.CSSLayout.Tests/CSSNodeTest.cs
Normal file
53
src/csharp/Facebook.CSSLayout.Tests/CSSNodeTest.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Facebook.CSSLayout.Tests
|
||||
{
|
||||
/**
|
||||
* Tests for {@link CSSNode}.
|
||||
*/
|
||||
public class CSSNodeTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void testAddChildGetParent()
|
||||
{
|
||||
CSSNode parent = new CSSNode();
|
||||
CSSNode child = new CSSNode();
|
||||
|
||||
Assert.Null(child.getParent());
|
||||
Assert.AreEqual(0, parent.getChildCount());
|
||||
|
||||
parent.addChildAt(child, 0);
|
||||
|
||||
Assert.AreEqual(1, parent.getChildCount());
|
||||
Assert.AreEqual(child, parent.getChildAt(0));
|
||||
Assert.AreEqual(parent, child.getParent());
|
||||
|
||||
parent.removeChildAt(0);
|
||||
|
||||
Assert.Null(child.getParent());
|
||||
Assert.AreEqual(0, parent.getChildCount());
|
||||
}
|
||||
|
||||
[Test, ExpectedException(typeof(InvalidOperationException))]
|
||||
public void testCannotAddChildToMultipleParents()
|
||||
{
|
||||
CSSNode parent1 = new CSSNode();
|
||||
CSSNode parent2 = new CSSNode();
|
||||
CSSNode child = new CSSNode();
|
||||
|
||||
parent1.addChildAt(child, 0);
|
||||
parent2.addChildAt(child, 0);
|
||||
}
|
||||
}
|
||||
}
|
70
src/csharp/Facebook.CSSLayout.Tests/Facebook.CSSLayout.Tests.csproj
Executable file
70
src/csharp/Facebook.CSSLayout.Tests/Facebook.CSSLayout.Tests.csproj
Executable file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E687C8FD-0A0D-450F-853D-EC301BE1C038}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Facebook.CSSLayout.Tests</RootNamespace>
|
||||
<AssemblyName>Facebook.CSSLayout.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CSSNodeTest.cs" />
|
||||
<Compile Include="LayoutCachingTest.cs" />
|
||||
<Compile Include="LayoutEngineTest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestConstants.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Facebook.CSSLayout\Facebook.CSSLayout.csproj">
|
||||
<Project>{d534fb4b-a7d4-4a29-96d3-f39a91a259bd}</Project>
|
||||
<Name>Facebook.CSSLayout</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
240
src/csharp/Facebook.CSSLayout.Tests/LayoutCachingTest.cs
Normal file
240
src/csharp/Facebook.CSSLayout.Tests/LayoutCachingTest.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Facebook.CSSLayout.Tests
|
||||
{
|
||||
|
||||
/**
|
||||
* Tests for {@link LayoutEngine} and {@link CSSNode} to make sure layouts are only generated when
|
||||
* needed.
|
||||
*/
|
||||
public class LayoutCachingTest
|
||||
{
|
||||
|
||||
private void assertTreeHasNewLayout(bool expectedHasNewLayout, CSSNode root)
|
||||
{
|
||||
Assert.AreEqual(expectedHasNewLayout, root.HasNewLayout);
|
||||
|
||||
for (int i = 0; i < root.getChildCount(); i++)
|
||||
{
|
||||
assertTreeHasNewLayout(expectedHasNewLayout, root.getChildAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void markLayoutAppliedForTree(CSSNode root)
|
||||
{
|
||||
root.MarkLayoutSeen();
|
||||
for (int i = 0; i < root.getChildCount(); i++)
|
||||
{
|
||||
markLayoutAppliedForTree(root.getChildAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testCachesFullTree()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
assertTreeHasNewLayout(true, root);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
root.calculateLayout();
|
||||
Assert.True(root.HasNewLayout);
|
||||
assertTreeHasNewLayout(false, c0);
|
||||
assertTreeHasNewLayout(false, c1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testInvalidatesCacheWhenChildAdded()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
CSSNode c0c1 = new CSSNode();
|
||||
CSSNode c1c0 = new CSSNode();
|
||||
c0c1.Width = 200;
|
||||
c0c1.Height = 200;
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
c0c0.addChildAt(c1c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c0.addChildAt(c0c1, 1);
|
||||
|
||||
root.calculateLayout();
|
||||
Assert.True(root.HasNewLayout);
|
||||
Assert.True(c0.HasNewLayout);
|
||||
Assert.True(c0c1.HasNewLayout);
|
||||
|
||||
Assert.True(c0c0.HasNewLayout);
|
||||
Assert.True(c1.HasNewLayout);
|
||||
|
||||
Assert.False(c1c0.HasNewLayout);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testInvalidatesCacheWhenEnumPropertyChanges()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c1.AlignSelf = CSSAlign.Center;
|
||||
root.calculateLayout();
|
||||
|
||||
Assert.True(root.HasNewLayout);
|
||||
Assert.True(c1.HasNewLayout);
|
||||
|
||||
Assert.True(c0.HasNewLayout);
|
||||
Assert.False(c0c0.HasNewLayout);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testInvalidatesCacheWhenFloatPropertyChanges()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c1.SetMargin(CSSSpacingType.Left, 10);
|
||||
root.calculateLayout();
|
||||
|
||||
Assert.True(root.HasNewLayout);
|
||||
Assert.True(c1.HasNewLayout);
|
||||
|
||||
Assert.True(c0.HasNewLayout);
|
||||
Assert.False(c0c0.HasNewLayout);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testInvalidatesFullTreeWhenParentWidthChanges()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
CSSNode c1c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
c1.addChildAt(c1c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c0.Height = 200;
|
||||
root.calculateLayout();
|
||||
|
||||
Assert.True(root.HasNewLayout);
|
||||
Assert.True(c0.HasNewLayout);
|
||||
Assert.True(c0c0.HasNewLayout);
|
||||
|
||||
Assert.True(c1.HasNewLayout);
|
||||
Assert.False(c1c0.HasNewLayout);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testDoesNotInvalidateCacheWhenPropertyIsTheSame()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
root.Width = 200;
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
root.Width = 200;
|
||||
root.calculateLayout();
|
||||
|
||||
Assert.True(root.HasNewLayout);
|
||||
assertTreeHasNewLayout(false, c0);
|
||||
assertTreeHasNewLayout(false, c1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testInvalidateCacheWhenHeightChangesPosition()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c1c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c1.addChildAt(c1c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c0.Height = 100;
|
||||
root.calculateLayout();
|
||||
|
||||
Assert.True(root.HasNewLayout);
|
||||
Assert.True(c0.HasNewLayout);
|
||||
Assert.True(c1.HasNewLayout);
|
||||
Assert.False(c1c0.HasNewLayout);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void testInvalidatesOnNewMeasureFunction()
|
||||
{
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
CSSNode c0c0 = new CSSNode();
|
||||
root.addChildAt(c0, 0);
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c1.setMeasureFunction((node, width) => new MeasureOutput(100, 20));
|
||||
|
||||
root.calculateLayout();
|
||||
|
||||
Assert.True(root.HasNewLayout);
|
||||
Assert.True(c1.HasNewLayout);
|
||||
|
||||
Assert.True(c0.HasNewLayout);
|
||||
Assert.False(c0c0.HasNewLayout);
|
||||
}
|
||||
}
|
||||
}
|
8196
src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs
Normal file
8196
src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs
Normal file
File diff suppressed because it is too large
Load Diff
27
src/csharp/Facebook.CSSLayout.Tests/Properties/AssemblyInfo.cs
Executable file
27
src/csharp/Facebook.CSSLayout.Tests/Properties/AssemblyInfo.cs
Executable file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Facebook.CSSLayout.Tests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Facebook.CSSLayout.Tests")]
|
||||
[assembly: AssemblyCopyright("Copyright © Facebook 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
[assembly: Guid("c186053a-741f-477d-b031-4d343fb20d1d")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
28
src/csharp/Facebook.CSSLayout.Tests/TestConstants.cs
Normal file
28
src/csharp/Facebook.CSSLayout.Tests/TestConstants.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout.Tests
|
||||
{
|
||||
/**
|
||||
* Generated constants used in {@link LayoutEngineTest}.
|
||||
*/
|
||||
public class TestConstants
|
||||
{
|
||||
|
||||
/** START_GENERATED **/
|
||||
public static readonly float SMALL_WIDTH = 35f;
|
||||
public static readonly float SMALL_HEIGHT = 18f;
|
||||
public static readonly float BIG_WIDTH = 172f;
|
||||
public static readonly float BIG_HEIGHT = 36f;
|
||||
public static readonly float BIG_MIN_WIDTH = 100f;
|
||||
public static readonly string SMALL_TEXT = "small";
|
||||
public static readonly string LONG_TEXT = "loooooooooong with space";
|
||||
/** END_GENERATED **/
|
||||
}
|
||||
}
|
4
src/csharp/Facebook.CSSLayout.Tests/packages.config
Executable file
4
src/csharp/Facebook.CSSLayout.Tests/packages.config
Executable file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="2.6.4" targetFramework="net45" />
|
||||
</packages>
|
34
src/csharp/Facebook.CSSLayout.sln
Executable file
34
src/csharp/Facebook.CSSLayout.sln
Executable file
@@ -0,0 +1,34 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facebook.CSSLayout", "Facebook.CSSLayout\Facebook.CSSLayout.csproj", "{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facebook.CSSLayout.Tests", "Facebook.CSSLayout.Tests\Facebook.CSSLayout.Tests.csproj", "{E687C8FD-0A0D-450F-853D-EC301BE1C038}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{29A6932B-FDDC-4E8A-8895-7FD64CC47B7F}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\CSharpTranspiler.js = ..\CSharpTranspiler.js
|
||||
..\JavaTranspiler.js = ..\JavaTranspiler.js
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
22
src/csharp/Facebook.CSSLayout/Assertions.cs
Executable file
22
src/csharp/Facebook.CSSLayout/Assertions.cs
Executable file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
static class Assertions
|
||||
{
|
||||
public static T assertNotNull<T>(T v) where T : class
|
||||
{
|
||||
Debug.Assert(v != null);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
20
src/csharp/Facebook.CSSLayout/CSSAlign.cs
Normal file
20
src/csharp/Facebook.CSSLayout/CSSAlign.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSAlign
|
||||
{
|
||||
Auto,
|
||||
FlexStart,
|
||||
Center,
|
||||
FlexEnd,
|
||||
Stretch,
|
||||
}
|
||||
}
|
21
src/csharp/Facebook.CSSLayout/CSSConstants.cs
Normal file
21
src/csharp/Facebook.CSSLayout/CSSConstants.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public static class CSSConstants
|
||||
{
|
||||
public static readonly float Undefined = float.NaN;
|
||||
|
||||
public static bool IsUndefined(float value)
|
||||
{
|
||||
return float.IsNaN(value);
|
||||
}
|
||||
}
|
||||
}
|
18
src/csharp/Facebook.CSSLayout/CSSDirection.cs
Executable file
18
src/csharp/Facebook.CSSLayout/CSSDirection.cs
Executable file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSDirection
|
||||
{
|
||||
Inherit,
|
||||
LTR,
|
||||
RTL
|
||||
}
|
||||
}
|
19
src/csharp/Facebook.CSSLayout/CSSFlexDirection.cs
Normal file
19
src/csharp/Facebook.CSSLayout/CSSFlexDirection.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSFlexDirection
|
||||
{
|
||||
Column,
|
||||
ColumnReverse,
|
||||
Row,
|
||||
RowReverse
|
||||
}
|
||||
}
|
20
src/csharp/Facebook.CSSLayout/CSSJustify.cs
Normal file
20
src/csharp/Facebook.CSSLayout/CSSJustify.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSJustify
|
||||
{
|
||||
FlexStart,
|
||||
Center,
|
||||
FlexEnd,
|
||||
SpaceBetween,
|
||||
SpaceAround
|
||||
}
|
||||
}
|
71
src/csharp/Facebook.CSSLayout/CSSLayout.cs
Normal file
71
src/csharp/Facebook.CSSLayout/CSSLayout.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
|
||||
/**
|
||||
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
|
||||
*/
|
||||
|
||||
class CSSLayout
|
||||
{
|
||||
public const int POSITION_LEFT = 0;
|
||||
public const int POSITION_TOP = 1;
|
||||
public const int POSITION_RIGHT = 2;
|
||||
public const int POSITION_BOTTOM = 3;
|
||||
|
||||
public const int DIMENSION_WIDTH = 0;
|
||||
public const int DIMENSION_HEIGHT = 1;
|
||||
|
||||
public float[] position = new float[4];
|
||||
public float[] dimensions = new float[2];
|
||||
public CSSDirection direction = CSSDirection.LTR;
|
||||
|
||||
/**
|
||||
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
|
||||
*/
|
||||
|
||||
public void resetResult()
|
||||
{
|
||||
FillArray(position, 0);
|
||||
FillArray(dimensions, CSSConstants.Undefined);
|
||||
|
||||
direction = CSSDirection.LTR;
|
||||
}
|
||||
|
||||
public void copy(CSSLayout layout)
|
||||
{
|
||||
position[POSITION_LEFT] = layout.position[POSITION_LEFT];
|
||||
position[POSITION_TOP] = layout.position[POSITION_TOP];
|
||||
position[POSITION_RIGHT] = layout.position[POSITION_RIGHT];
|
||||
position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM];
|
||||
dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH];
|
||||
dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT];
|
||||
direction = layout.direction;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "layout: {" +
|
||||
"left: " + position[POSITION_LEFT] + ", " +
|
||||
"top: " + position[POSITION_TOP] + ", " +
|
||||
"width: " + dimensions[DIMENSION_WIDTH] + ", " +
|
||||
"height: " + dimensions[DIMENSION_HEIGHT] + ", " +
|
||||
"direction: " + direction +
|
||||
"}";
|
||||
}
|
||||
|
||||
static void FillArray<T>(T[] array, T value)
|
||||
{
|
||||
for (var i = 0; i != array.Length; ++i)
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
26
src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs
Executable file
26
src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs
Executable file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
|
||||
/**
|
||||
* A context for holding values local to a given instance of layout computation.
|
||||
*
|
||||
* This is necessary for making layout thread-safe. A separate instance should
|
||||
* be used when {@link CSSNode#calculateLayout} is called concurrently on
|
||||
* different node hierarchies.
|
||||
*/
|
||||
|
||||
sealed class CSSLayoutContext
|
||||
{
|
||||
/*package*/
|
||||
public MeasureOutput measureOutput = new MeasureOutput();
|
||||
}
|
||||
}
|
542
src/csharp/Facebook.CSSLayout/CSSNode.cs
Normal file
542
src/csharp/Facebook.CSSLayout/CSSNode.cs
Normal file
@@ -0,0 +1,542 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
/**
|
||||
* Should measure the given node and put the result in the given MeasureOutput.
|
||||
*/
|
||||
|
||||
public delegate MeasureOutput MeasureFunction(CSSNode node, float width);
|
||||
|
||||
/**
|
||||
* A CSS Node. It has a style object you can manipulate at {@link #style}. After calling
|
||||
* {@link #calculateLayout()}, {@link #layout} will be filled with the results of the layout.
|
||||
*/
|
||||
|
||||
public class CSSNode
|
||||
{
|
||||
const int POSITION_LEFT = CSSLayout.POSITION_LEFT;
|
||||
const int POSITION_TOP = CSSLayout.POSITION_TOP;
|
||||
const int POSITION_RIGHT = CSSLayout.POSITION_RIGHT;
|
||||
const int POSITION_BOTTOM = CSSLayout.POSITION_BOTTOM;
|
||||
const int DIMENSION_WIDTH = CSSLayout.DIMENSION_WIDTH;
|
||||
const int DIMENSION_HEIGHT = CSSLayout.DIMENSION_HEIGHT;
|
||||
|
||||
enum LayoutState
|
||||
{
|
||||
/**
|
||||
* Some property of this node or its children has changes and the current values in
|
||||
* {@link #layout} are not valid.
|
||||
*/
|
||||
DIRTY,
|
||||
|
||||
/**
|
||||
* This node has a new layout relative to the last time {@link #MarkLayoutSeen()} was called.
|
||||
*/
|
||||
HAS_NEW_LAYOUT,
|
||||
|
||||
/**
|
||||
* {@link #layout} is valid for the node's properties and this layout has been marked as
|
||||
* having been seen.
|
||||
*/
|
||||
UP_TO_DATE,
|
||||
}
|
||||
|
||||
internal readonly CSSStyle style = new CSSStyle();
|
||||
internal readonly CSSLayout layout = new CSSLayout();
|
||||
internal readonly CachedCSSLayout lastLayout = new CachedCSSLayout();
|
||||
|
||||
internal int lineIndex = 0;
|
||||
internal /*package*/ CSSNode nextAbsoluteChild;
|
||||
internal /*package*/ CSSNode nextFlexChild;
|
||||
|
||||
// 4 is kinda arbitrary, but the default of 10 seems really high for an average View.
|
||||
readonly List<CSSNode> mChildren = new List<CSSNode>(4);
|
||||
[Nullable] CSSNode mParent;
|
||||
[Nullable] MeasureFunction mMeasureFunction = null;
|
||||
LayoutState mLayoutState = LayoutState.DIRTY;
|
||||
|
||||
public int ChildCount
|
||||
{
|
||||
get { return mChildren.Count; }
|
||||
}
|
||||
|
||||
public CSSNode this[int i]
|
||||
{
|
||||
get { return mChildren[i]; }
|
||||
}
|
||||
|
||||
public IEnumerable<CSSNode> Children
|
||||
{
|
||||
get { return mChildren; }
|
||||
}
|
||||
|
||||
public void AddChild(CSSNode child)
|
||||
{
|
||||
InsertChild(ChildCount, child);
|
||||
}
|
||||
|
||||
public void InsertChild(int i, CSSNode child)
|
||||
{
|
||||
if (child.mParent != null)
|
||||
{
|
||||
throw new InvalidOperationException("Child already has a parent, it must be removed first.");
|
||||
}
|
||||
|
||||
mChildren.Insert(i, child);
|
||||
child.mParent = this;
|
||||
dirty();
|
||||
}
|
||||
|
||||
public void RemoveChildAt(int i)
|
||||
{
|
||||
mChildren[i].mParent = null;
|
||||
mChildren.RemoveAt(i);
|
||||
dirty();
|
||||
}
|
||||
|
||||
public CSSNode Parent
|
||||
{
|
||||
[return: Nullable]
|
||||
get
|
||||
{ return mParent; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the index of the given child, or -1 if the child doesn't exist in this node.
|
||||
*/
|
||||
|
||||
public int IndexOf(CSSNode child)
|
||||
{
|
||||
return mChildren.IndexOf(child);
|
||||
}
|
||||
|
||||
public MeasureFunction MeasureFunction
|
||||
{
|
||||
get { return mMeasureFunction; }
|
||||
set
|
||||
{
|
||||
if (!valuesEqual(mMeasureFunction, value))
|
||||
{
|
||||
mMeasureFunction = value;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMeasureDefined
|
||||
{
|
||||
get { return mMeasureFunction != null; }
|
||||
}
|
||||
|
||||
internal MeasureOutput measure(MeasureOutput measureOutput, float width)
|
||||
{
|
||||
if (!IsMeasureDefined)
|
||||
{
|
||||
throw new Exception("Measure function isn't defined!");
|
||||
}
|
||||
return Assertions.assertNotNull(mMeasureFunction)(this, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual layout and saves the results in {@link #layout}
|
||||
*/
|
||||
|
||||
public void CalculateLayout()
|
||||
{
|
||||
layout.resetResult();
|
||||
LayoutEngine.layoutNode(DummyLayoutContext, this, CSSConstants.Undefined, null);
|
||||
}
|
||||
|
||||
static readonly CSSLayoutContext DummyLayoutContext = new CSSLayoutContext();
|
||||
|
||||
/**
|
||||
* See {@link LayoutState#DIRTY}.
|
||||
*/
|
||||
|
||||
public bool IsDirty
|
||||
{
|
||||
get { return mLayoutState == LayoutState.DIRTY; }
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link LayoutState#HAS_NEW_LAYOUT}.
|
||||
*/
|
||||
|
||||
public bool HasNewLayout
|
||||
{
|
||||
get { return mLayoutState == LayoutState.HAS_NEW_LAYOUT; }
|
||||
}
|
||||
|
||||
internal protected void dirty()
|
||||
{
|
||||
if (mLayoutState == LayoutState.DIRTY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (mLayoutState == LayoutState.HAS_NEW_LAYOUT)
|
||||
{
|
||||
throw new InvalidOperationException("Previous layout was ignored! MarkLayoutSeen() never called");
|
||||
}
|
||||
|
||||
mLayoutState = LayoutState.DIRTY;
|
||||
|
||||
if (mParent != null)
|
||||
{
|
||||
mParent.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
internal void markHasNewLayout()
|
||||
{
|
||||
mLayoutState = LayoutState.HAS_NEW_LAYOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the node that the current values in {@link #layout} have been seen. Subsequent calls
|
||||
* to {@link #hasNewLayout()} will return false until this node is laid out with new parameters.
|
||||
* You must call this each time the layout is generated if the node has a new layout.
|
||||
*/
|
||||
|
||||
public void MarkLayoutSeen()
|
||||
{
|
||||
if (!HasNewLayout)
|
||||
{
|
||||
throw new InvalidOperationException("Expected node to have a new layout to be seen!");
|
||||
}
|
||||
|
||||
mLayoutState = LayoutState.UP_TO_DATE;
|
||||
}
|
||||
|
||||
void toStringWithIndentation(StringBuilder result, int level)
|
||||
{
|
||||
// Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
|
||||
StringBuilder indentation = new StringBuilder();
|
||||
for (int i = 0; i < level; ++i)
|
||||
{
|
||||
indentation.Append("__");
|
||||
}
|
||||
|
||||
result.Append(indentation.ToString());
|
||||
result.Append(layout.ToString());
|
||||
|
||||
if (ChildCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
result.Append(", children: [\n");
|
||||
for (var i = 0; i < ChildCount; i++)
|
||||
{
|
||||
this[i].toStringWithIndentation(result, level + 1);
|
||||
result.Append("\n");
|
||||
}
|
||||
result.Append(indentation + "]");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
this.toStringWithIndentation(sb, 0);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
protected bool valuesEqual(float f1, float f2)
|
||||
{
|
||||
return FloatUtil.floatsEqual(f1, f2);
|
||||
}
|
||||
|
||||
protected bool valuesEqual<T>([Nullable] T o1, [Nullable] T o2)
|
||||
{
|
||||
if (o1 == null)
|
||||
{
|
||||
return o2 == null;
|
||||
}
|
||||
return o1.Equals(o2);
|
||||
}
|
||||
|
||||
public CSSDirection Direction
|
||||
{
|
||||
get { return style.direction; }
|
||||
set { updateDiscreteValue(ref style.direction, value); }
|
||||
}
|
||||
|
||||
public CSSFlexDirection FlexDirection
|
||||
{
|
||||
get { return style.flexDirection; }
|
||||
set { updateDiscreteValue(ref style.flexDirection, value); }
|
||||
}
|
||||
|
||||
public CSSJustify JustifyContent
|
||||
{
|
||||
get { return style.justifyContent; }
|
||||
set { updateDiscreteValue(ref style.justifyContent, value); }
|
||||
}
|
||||
|
||||
public CSSAlign AlignContent
|
||||
{
|
||||
get { return style.alignContent; }
|
||||
set { updateDiscreteValue(ref style.alignContent, value); }
|
||||
}
|
||||
|
||||
public CSSAlign AlignItems
|
||||
{
|
||||
get { return style.alignItems; }
|
||||
set { updateDiscreteValue(ref style.alignItems, value); }
|
||||
}
|
||||
|
||||
public CSSAlign AlignSelf
|
||||
{
|
||||
get { return style.alignSelf; }
|
||||
set { updateDiscreteValue(ref style.alignSelf, value); }
|
||||
}
|
||||
|
||||
public CSSPositionType PositionType
|
||||
{
|
||||
get { return style.positionType; }
|
||||
set { updateDiscreteValue(ref style.positionType, value); }
|
||||
}
|
||||
|
||||
public CSSWrap Wrap
|
||||
{
|
||||
get { return style.flexWrap; }
|
||||
set { updateDiscreteValue(ref style.flexWrap, value); }
|
||||
}
|
||||
|
||||
public float Flex
|
||||
{
|
||||
get { return style.flex; }
|
||||
set { updateFloatValue(ref style.flex, value); }
|
||||
}
|
||||
|
||||
public void SetMargin(CSSSpacingType spacingType, float margin)
|
||||
{
|
||||
if (style.margin.set((int)spacingType, margin))
|
||||
dirty();
|
||||
}
|
||||
|
||||
public float GetMargin(CSSSpacingType spacingType)
|
||||
{
|
||||
return style.margin.getRaw((int)spacingType);
|
||||
}
|
||||
|
||||
public void SetPadding(CSSSpacingType spacingType, float padding)
|
||||
{
|
||||
if (style.padding.set((int)spacingType, padding))
|
||||
dirty();
|
||||
}
|
||||
|
||||
public float GetPadding(CSSSpacingType spacingType)
|
||||
{
|
||||
return style.padding.getRaw((int)spacingType);
|
||||
}
|
||||
|
||||
public void SetBorder(CSSSpacingType spacingType, float border)
|
||||
{
|
||||
if (style.border.set((int)spacingType, border))
|
||||
dirty();
|
||||
}
|
||||
|
||||
public float GetBorder(CSSSpacingType spacingType)
|
||||
{
|
||||
return style.border.getRaw((int)spacingType);
|
||||
}
|
||||
|
||||
public float PositionTop
|
||||
{
|
||||
get { return style.position[POSITION_TOP]; }
|
||||
set { updateFloatValue(ref style.position[POSITION_TOP], value); }
|
||||
}
|
||||
|
||||
public float PositionBottom
|
||||
{
|
||||
get { return style.position[POSITION_BOTTOM]; }
|
||||
set { updateFloatValue(ref style.position[POSITION_BOTTOM], value); }
|
||||
}
|
||||
|
||||
public float PositionLeft
|
||||
{
|
||||
get { return style.position[POSITION_LEFT]; }
|
||||
set { updateFloatValue(ref style.position[POSITION_LEFT], value); }
|
||||
}
|
||||
|
||||
public float PositionRight
|
||||
{
|
||||
get { return style.position[POSITION_RIGHT]; }
|
||||
set { updateFloatValue(ref style.position[POSITION_RIGHT], value); }
|
||||
}
|
||||
|
||||
public float Width
|
||||
{
|
||||
get { return style.dimensions[DIMENSION_WIDTH]; }
|
||||
set { updateFloatValue(ref style.dimensions[DIMENSION_WIDTH], value); }
|
||||
}
|
||||
|
||||
public float Height
|
||||
{
|
||||
get { return style.dimensions[DIMENSION_HEIGHT]; }
|
||||
set { updateFloatValue(ref style.dimensions[DIMENSION_HEIGHT], value); }
|
||||
}
|
||||
|
||||
public float MinWidth
|
||||
{
|
||||
get { return style.minWidth; }
|
||||
set { updateFloatValue(ref style.minWidth, value); }
|
||||
}
|
||||
|
||||
public float MinHeight
|
||||
{
|
||||
get { return style.minHeight; }
|
||||
set { updateFloatValue(ref style.minHeight, value); }
|
||||
}
|
||||
|
||||
public float MaxWidth
|
||||
{
|
||||
get { return style.maxWidth; }
|
||||
set { updateFloatValue(ref style.maxWidth, value); }
|
||||
}
|
||||
|
||||
public float MaxHeight
|
||||
{
|
||||
get { return style.maxHeight; }
|
||||
set { updateFloatValue(ref style.maxHeight, value); }
|
||||
}
|
||||
|
||||
public float LayoutX
|
||||
{
|
||||
get { return layout.position[POSITION_LEFT]; }
|
||||
}
|
||||
|
||||
public float LayoutY
|
||||
{
|
||||
get { return layout.position[POSITION_TOP]; }
|
||||
}
|
||||
|
||||
public float LayoutWidth
|
||||
{
|
||||
get { return layout.dimensions[DIMENSION_WIDTH]; }
|
||||
}
|
||||
|
||||
public float LayoutHeight
|
||||
{
|
||||
get { return layout.dimensions[DIMENSION_HEIGHT]; }
|
||||
}
|
||||
|
||||
public CSSDirection LayoutDirection
|
||||
{
|
||||
get { return layout.direction; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a default padding (left/top/right/bottom) for this node.
|
||||
*/
|
||||
public void SetDefaultPadding(CSSSpacingType spacingType, float padding)
|
||||
{
|
||||
if (style.padding.setDefault((int)spacingType, padding))
|
||||
dirty();
|
||||
}
|
||||
|
||||
void updateDiscreteValue<ValueT>(ref ValueT valueRef, ValueT newValue)
|
||||
{
|
||||
if (valuesEqual(valueRef, newValue))
|
||||
return;
|
||||
|
||||
valueRef = newValue;
|
||||
dirty();
|
||||
}
|
||||
|
||||
void updateFloatValue(ref float valueRef, float newValue)
|
||||
{
|
||||
if (valuesEqual(valueRef, newValue))
|
||||
return;
|
||||
valueRef = newValue;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CSSNodeExtensions
|
||||
{
|
||||
/*
|
||||
Explicitly mark this node as dirty.
|
||||
|
||||
Calling this function is required when the measure function points to the same instance,
|
||||
but changes its behavior.
|
||||
|
||||
For all other property changes, the node is automatically marked dirty.
|
||||
*/
|
||||
|
||||
public static void MarkDirty(this CSSNode node)
|
||||
{
|
||||
node.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CSSNodeExtensionsInternal
|
||||
{
|
||||
public static CSSNode getParent(this CSSNode node)
|
||||
{
|
||||
return node.Parent;
|
||||
}
|
||||
|
||||
public static int getChildCount(this CSSNode node)
|
||||
{
|
||||
return node.ChildCount;
|
||||
}
|
||||
|
||||
public static CSSNode getChildAt(this CSSNode node, int i)
|
||||
{
|
||||
return node[i];
|
||||
}
|
||||
|
||||
public static void addChildAt(this CSSNode node, CSSNode child, int i)
|
||||
{
|
||||
node.InsertChild(i, child);
|
||||
}
|
||||
|
||||
public static void removeChildAt(this CSSNode node, int i)
|
||||
{
|
||||
node.RemoveChildAt(i);
|
||||
}
|
||||
|
||||
public static void setMeasureFunction(this CSSNode node, MeasureFunction measureFunction)
|
||||
{
|
||||
node.MeasureFunction = measureFunction;
|
||||
}
|
||||
|
||||
public static void calculateLayout(this CSSNode node)
|
||||
{
|
||||
node.CalculateLayout();
|
||||
}
|
||||
|
||||
public static bool isDirty(this CSSNode node)
|
||||
{
|
||||
return node.IsDirty;
|
||||
}
|
||||
|
||||
public static void setMargin(this CSSNode node, int spacingType, float margin)
|
||||
{
|
||||
node.SetMargin((CSSSpacingType)spacingType, margin);
|
||||
}
|
||||
|
||||
public static void setPadding(this CSSNode node, int spacingType, float padding)
|
||||
{
|
||||
node.SetPadding((CSSSpacingType)spacingType, padding);
|
||||
}
|
||||
|
||||
public static void setBorder(this CSSNode node, int spacingType, float border)
|
||||
{
|
||||
node.SetBorder((CSSSpacingType)spacingType, border);
|
||||
}
|
||||
}
|
||||
}
|
17
src/csharp/Facebook.CSSLayout/CSSPositionType.cs
Normal file
17
src/csharp/Facebook.CSSLayout/CSSPositionType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSPositionType
|
||||
{
|
||||
Relative,
|
||||
Absolute
|
||||
}
|
||||
}
|
24
src/csharp/Facebook.CSSLayout/CSSSpacingType.cs
Executable file
24
src/csharp/Facebook.CSSLayout/CSSSpacingType.cs
Executable file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSSpacingType
|
||||
{
|
||||
Left = 0,
|
||||
Top = 1,
|
||||
Right = 2,
|
||||
Bottom = 3,
|
||||
Vertical = 4,
|
||||
Horizontal = 5,
|
||||
Start = 6,
|
||||
End = 7,
|
||||
All = 8
|
||||
}
|
||||
}
|
49
src/csharp/Facebook.CSSLayout/CSSStyle.cs
Normal file
49
src/csharp/Facebook.CSSLayout/CSSStyle.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
/**
|
||||
* The CSS style definition for a {@link CSSNode}.
|
||||
*/
|
||||
sealed class CSSStyle
|
||||
{
|
||||
public CSSDirection direction = CSSDirection.Inherit;
|
||||
public CSSFlexDirection flexDirection = CSSFlexDirection.Column;
|
||||
public CSSJustify justifyContent = CSSJustify.FlexStart;
|
||||
public CSSAlign alignContent = CSSAlign.FlexStart;
|
||||
public CSSAlign alignItems = CSSAlign.Stretch;
|
||||
public CSSAlign alignSelf = CSSAlign.Auto;
|
||||
public CSSPositionType positionType = CSSPositionType.Relative;
|
||||
public CSSWrap flexWrap = CSSWrap.NoWrap;
|
||||
public float flex;
|
||||
|
||||
public Spacing margin = new Spacing();
|
||||
public Spacing padding = new Spacing();
|
||||
public Spacing border = new Spacing();
|
||||
|
||||
public float[] position = {
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined
|
||||
};
|
||||
|
||||
public float[] dimensions = {
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined
|
||||
};
|
||||
|
||||
public float minWidth = CSSConstants.Undefined;
|
||||
public float minHeight = CSSConstants.Undefined;
|
||||
|
||||
public float maxWidth = CSSConstants.Undefined;
|
||||
public float maxHeight = CSSConstants.Undefined;
|
||||
}
|
||||
}
|
16
src/csharp/Facebook.CSSLayout/CSSWrap.cs
Normal file
16
src/csharp/Facebook.CSSLayout/CSSWrap.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
public enum CSSWrap
|
||||
{
|
||||
NoWrap,
|
||||
Wrap
|
||||
}
|
||||
}
|
24
src/csharp/Facebook.CSSLayout/CachedCSSLayout.cs
Normal file
24
src/csharp/Facebook.CSSLayout/CachedCSSLayout.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
/**
|
||||
* CSSLayout with additional information about the conditions under which it was generated.
|
||||
* {@link #RequestedWidth} and {@link #RequestedHeight} are the width and height the parent set on
|
||||
* this node before calling layout visited us.
|
||||
*/
|
||||
|
||||
class CachedCSSLayout : CSSLayout
|
||||
{
|
||||
public float requestedWidth = CSSConstants.Undefined;
|
||||
public float requestedHeight = CSSConstants.Undefined;
|
||||
public float parentMaxWidth = CSSConstants.Undefined;
|
||||
}
|
||||
}
|
70
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj
Executable file
70
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj
Executable file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Facebook.CSSLayout</RootNamespace>
|
||||
<AssemblyName>Facebook.CSSLayout</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<LangVersion>5</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Assertions.cs" />
|
||||
<Compile Include="CachedCSSLayout.cs" />
|
||||
<Compile Include="CSSAlign.cs" />
|
||||
<Compile Include="CSSConstants.cs" />
|
||||
<Compile Include="CSSDirection.cs" />
|
||||
<Compile Include="CSSFlexDirection.cs" />
|
||||
<Compile Include="CSSJustify.cs" />
|
||||
<Compile Include="CSSLayout.cs" />
|
||||
<Compile Include="CSSLayoutContext.cs" />
|
||||
<Compile Include="CSSNode.cs" />
|
||||
<Compile Include="CSSPositionType.cs" />
|
||||
<Compile Include="CSSStyle.cs" />
|
||||
<Compile Include="CSSWrap.cs" />
|
||||
<Compile Include="FloatUtil.cs" />
|
||||
<Compile Include="LayoutEngine.cs" />
|
||||
<Compile Include="MeasureOutput.cs" />
|
||||
<Compile Include="NullableAttribute.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Spacing.cs" />
|
||||
<Compile Include="CSSSpacingType.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
17
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.nuspec
Executable file
17
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.nuspec
Executable file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>$id$</id>
|
||||
<version>$version$</version>
|
||||
<title>$title$</title>
|
||||
<authors>$author$</authors>
|
||||
<owners>$author$</owners>
|
||||
<licenseUrl>https://github.com/facebook/css-layout/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/facebook/css-layout</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>$description$</description>
|
||||
<releaseNotes></releaseNotes>
|
||||
<copyright>Copyright 2015 Facebook</copyright>
|
||||
<tags>flexbox flex-box css layout css-layout facebook</tags>
|
||||
</metadata>
|
||||
</package>
|
27
src/csharp/Facebook.CSSLayout/FloatUtil.cs
Normal file
27
src/csharp/Facebook.CSSLayout/FloatUtil.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
static class FloatUtil
|
||||
{
|
||||
const float Epsilon = .00001f;
|
||||
|
||||
public static bool floatsEqual(float f1, float f2)
|
||||
{
|
||||
if (float.IsNaN(f1) || float.IsNaN(f2))
|
||||
{
|
||||
return float.IsNaN(f1) && float.IsNaN(f2);
|
||||
}
|
||||
return Math.Abs(f2 - f1) < Epsilon;
|
||||
}
|
||||
}
|
||||
}
|
937
src/csharp/Facebook.CSSLayout/LayoutEngine.cs
Normal file
937
src/csharp/Facebook.CSSLayout/LayoutEngine.cs
Normal file
@@ -0,0 +1,937 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using boolean = System.Boolean;
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
|
||||
/**
|
||||
* Calculates layouts based on CSS style. See {@link #layoutNode(CSSNode, float)}.
|
||||
*/
|
||||
|
||||
static class LayoutEngine
|
||||
{
|
||||
const int POSITION_LEFT = CSSLayout.POSITION_LEFT;
|
||||
const int POSITION_TOP = CSSLayout.POSITION_TOP;
|
||||
const int POSITION_RIGHT = CSSLayout.POSITION_RIGHT;
|
||||
const int POSITION_BOTTOM = CSSLayout.POSITION_BOTTOM;
|
||||
const int DIMENSION_WIDTH = CSSLayout.DIMENSION_WIDTH;
|
||||
const int DIMENSION_HEIGHT = CSSLayout.DIMENSION_HEIGHT;
|
||||
|
||||
const int CSS_FLEX_DIRECTION_COLUMN =
|
||||
(int)CSSFlexDirection.Column;
|
||||
const int CSS_FLEX_DIRECTION_COLUMN_REVERSE =
|
||||
(int)CSSFlexDirection.ColumnReverse;
|
||||
const int CSS_FLEX_DIRECTION_ROW =
|
||||
(int)CSSFlexDirection.Row;
|
||||
const int CSS_FLEX_DIRECTION_ROW_REVERSE =
|
||||
(int)CSSFlexDirection.RowReverse;
|
||||
|
||||
const int CSS_POSITION_RELATIVE = (int)CSSPositionType.Relative;
|
||||
const int CSS_POSITION_ABSOLUTE = (int)CSSPositionType.Absolute;
|
||||
|
||||
private static readonly int[] leading = {
|
||||
POSITION_TOP,
|
||||
POSITION_BOTTOM,
|
||||
POSITION_LEFT,
|
||||
POSITION_RIGHT,
|
||||
};
|
||||
|
||||
private static readonly int[] trailing = {
|
||||
POSITION_BOTTOM,
|
||||
POSITION_TOP,
|
||||
POSITION_RIGHT,
|
||||
POSITION_LEFT,
|
||||
};
|
||||
|
||||
private static readonly int[] pos = {
|
||||
POSITION_TOP,
|
||||
POSITION_BOTTOM,
|
||||
POSITION_LEFT,
|
||||
POSITION_RIGHT,
|
||||
};
|
||||
|
||||
private static readonly int[] dim = {
|
||||
DIMENSION_HEIGHT,
|
||||
DIMENSION_HEIGHT,
|
||||
DIMENSION_WIDTH,
|
||||
DIMENSION_WIDTH,
|
||||
};
|
||||
|
||||
private static readonly int[] leadingSpacing = {
|
||||
Spacing.TOP,
|
||||
Spacing.BOTTOM,
|
||||
Spacing.START,
|
||||
Spacing.START
|
||||
};
|
||||
|
||||
private static readonly int[] trailingSpacing = {
|
||||
Spacing.BOTTOM,
|
||||
Spacing.TOP,
|
||||
Spacing.END,
|
||||
Spacing.END
|
||||
};
|
||||
|
||||
private static float boundAxis(CSSNode node, int axis, float value)
|
||||
{
|
||||
float min = CSSConstants.Undefined;
|
||||
float max = CSSConstants.Undefined;
|
||||
|
||||
if (axis == CSS_FLEX_DIRECTION_COLUMN || axis == CSS_FLEX_DIRECTION_COLUMN_REVERSE)
|
||||
{
|
||||
min = node.style.minHeight;
|
||||
max = node.style.maxHeight;
|
||||
}
|
||||
else if (axis == CSS_FLEX_DIRECTION_ROW || axis == CSS_FLEX_DIRECTION_ROW_REVERSE)
|
||||
{
|
||||
min = node.style.minWidth;
|
||||
max = node.style.maxWidth;
|
||||
}
|
||||
|
||||
float boundValue = value;
|
||||
|
||||
if (!float.IsNaN(max) && max >= 0.0 && boundValue > max)
|
||||
{
|
||||
boundValue = max;
|
||||
}
|
||||
if (!float.IsNaN(min) && min >= 0.0 && boundValue < min)
|
||||
{
|
||||
boundValue = min;
|
||||
}
|
||||
|
||||
return boundValue;
|
||||
}
|
||||
|
||||
private static void setDimensionFromStyle(CSSNode node, int axis)
|
||||
{
|
||||
// The parent already computed us a width or height. We just skip it
|
||||
if (!float.IsNaN(node.layout.dimensions[dim[axis]]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// We only run if there's a width or height defined
|
||||
if (float.IsNaN(node.style.dimensions[dim[axis]]) ||
|
||||
node.style.dimensions[dim[axis]] <= 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The dimensions can never be smaller than the padding and border
|
||||
float maxLayoutDimension = Math.Max(
|
||||
boundAxis(node, axis, node.style.dimensions[dim[axis]]),
|
||||
node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) +
|
||||
node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) +
|
||||
node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) +
|
||||
node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]));
|
||||
node.layout.dimensions[dim[axis]] = maxLayoutDimension;
|
||||
}
|
||||
|
||||
private static float getRelativePosition(CSSNode node, int axis)
|
||||
{
|
||||
float lead = node.style.position[leading[axis]];
|
||||
if (!float.IsNaN(lead))
|
||||
{
|
||||
return lead;
|
||||
}
|
||||
|
||||
float trailingPos = node.style.position[trailing[axis]];
|
||||
return float.IsNaN(trailingPos) ? 0 : -trailingPos;
|
||||
}
|
||||
|
||||
static int resolveAxis(int axis, CSSDirection direction)
|
||||
{
|
||||
if (direction == CSSDirection.RTL)
|
||||
{
|
||||
if (axis == CSS_FLEX_DIRECTION_ROW)
|
||||
{
|
||||
return CSS_FLEX_DIRECTION_ROW_REVERSE;
|
||||
}
|
||||
else if (axis == CSS_FLEX_DIRECTION_ROW_REVERSE)
|
||||
{
|
||||
return CSS_FLEX_DIRECTION_ROW;
|
||||
}
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
static CSSDirection resolveDirection(CSSNode node, CSSDirection? parentDirection)
|
||||
{
|
||||
CSSDirection direction = node.style.direction;
|
||||
if (direction == CSSDirection.Inherit)
|
||||
{
|
||||
direction = (parentDirection == null ? CSSDirection.LTR : parentDirection.Value);
|
||||
}
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
static int getFlexDirection(CSSNode node)
|
||||
{
|
||||
return (int)node.style.flexDirection;
|
||||
}
|
||||
|
||||
private static int getCrossFlexDirection(int axis, CSSDirection direction)
|
||||
{
|
||||
if (axis == CSS_FLEX_DIRECTION_COLUMN || axis == CSS_FLEX_DIRECTION_COLUMN_REVERSE)
|
||||
{
|
||||
return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CSS_FLEX_DIRECTION_COLUMN;
|
||||
}
|
||||
}
|
||||
|
||||
static CSSAlign getAlignItem(CSSNode node, CSSNode child)
|
||||
{
|
||||
if (child.style.alignSelf != CSSAlign.Auto)
|
||||
{
|
||||
return child.style.alignSelf;
|
||||
}
|
||||
return node.style.alignItems;
|
||||
}
|
||||
|
||||
static boolean isMeasureDefined(CSSNode node)
|
||||
{
|
||||
return node.IsMeasureDefined;
|
||||
}
|
||||
|
||||
static boolean needsRelayout(CSSNode node, float parentMaxWidth)
|
||||
{
|
||||
return node.isDirty() ||
|
||||
!FloatUtil.floatsEqual(
|
||||
node.lastLayout.requestedHeight,
|
||||
node.layout.dimensions[DIMENSION_HEIGHT]) ||
|
||||
!FloatUtil.floatsEqual(
|
||||
node.lastLayout.requestedWidth,
|
||||
node.layout.dimensions[DIMENSION_WIDTH]) ||
|
||||
!FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth);
|
||||
}
|
||||
|
||||
internal static void layoutNode(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, CSSDirection? parentDirection)
|
||||
{
|
||||
if (needsRelayout(node, parentMaxWidth))
|
||||
{
|
||||
node.lastLayout.requestedWidth = node.layout.dimensions[DIMENSION_WIDTH];
|
||||
node.lastLayout.requestedHeight = node.layout.dimensions[DIMENSION_HEIGHT];
|
||||
node.lastLayout.parentMaxWidth = parentMaxWidth;
|
||||
|
||||
layoutNodeImpl(layoutContext, node, parentMaxWidth, parentDirection);
|
||||
node.lastLayout.copy(node.layout);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.layout.copy(node.lastLayout);
|
||||
}
|
||||
|
||||
node.markHasNewLayout();
|
||||
}
|
||||
|
||||
static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, CSSDirection? parentDirection)
|
||||
{
|
||||
var childCount_ = node.getChildCount();
|
||||
for (int i_ = 0; i_ < childCount_; i_++)
|
||||
{
|
||||
node.getChildAt(i_).layout.resetResult();
|
||||
}
|
||||
|
||||
|
||||
/** START_GENERATED **/
|
||||
|
||||
CSSDirection direction = resolveDirection(node, parentDirection);
|
||||
int mainAxis = resolveAxis(getFlexDirection(node), direction);
|
||||
int crossAxis = getCrossFlexDirection(mainAxis, direction);
|
||||
int resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
|
||||
|
||||
// Handle width and height style attributes
|
||||
setDimensionFromStyle(node, mainAxis);
|
||||
setDimensionFromStyle(node, crossAxis);
|
||||
|
||||
// Set the resolved resolution in the node's layout
|
||||
node.layout.direction = direction;
|
||||
|
||||
// The position is set by the parent, but we need to complete it with a
|
||||
// delta composed of the margin and left/top/right/bottom
|
||||
node.layout.position[leading[mainAxis]] += node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
|
||||
getRelativePosition(node, mainAxis);
|
||||
node.layout.position[trailing[mainAxis]] += node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) +
|
||||
getRelativePosition(node, mainAxis);
|
||||
node.layout.position[leading[crossAxis]] += node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
|
||||
getRelativePosition(node, crossAxis);
|
||||
node.layout.position[trailing[crossAxis]] += node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) +
|
||||
getRelativePosition(node, crossAxis);
|
||||
|
||||
// Inline immutable values from the target node to avoid excessive method
|
||||
// invocations during the layout calculation.
|
||||
int childCount = node.getChildCount();
|
||||
float paddingAndBorderAxisResolvedRow = ((node.style.padding.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.border.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis])) + (node.style.padding.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]) + node.style.border.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])));
|
||||
|
||||
if (isMeasureDefined(node)) {
|
||||
boolean isResolvedRowDimDefined = !float.IsNaN(node.layout.dimensions[dim[resolvedRowAxis]]);
|
||||
|
||||
float width = CSSConstants.Undefined;
|
||||
if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
|
||||
width = node.style.dimensions[DIMENSION_WIDTH];
|
||||
} else if (isResolvedRowDimDefined) {
|
||||
width = node.layout.dimensions[dim[resolvedRowAxis]];
|
||||
} else {
|
||||
width = parentMaxWidth -
|
||||
(node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]));
|
||||
}
|
||||
width -= paddingAndBorderAxisResolvedRow;
|
||||
|
||||
// We only need to give a dimension for the text if we haven't got any
|
||||
// for it computed yet. It can either be from the style attribute or because
|
||||
// the element is flexible.
|
||||
boolean isRowUndefined = !(!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0) && !isResolvedRowDimDefined;
|
||||
boolean isColumnUndefined = !(!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) &&
|
||||
float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
|
||||
|
||||
// Let's not measure the text if we already know both dimensions
|
||||
if (isRowUndefined || isColumnUndefined) {
|
||||
MeasureOutput measureDim = node.measure(
|
||||
|
||||
layoutContext.measureOutput,
|
||||
width
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
node.layout.dimensions[DIMENSION_WIDTH] = measureDim.width +
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
if (isColumnUndefined) {
|
||||
node.layout.dimensions[DIMENSION_HEIGHT] = measureDim.height +
|
||||
((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
|
||||
}
|
||||
}
|
||||
if (childCount == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.Wrap);
|
||||
|
||||
CSSJustify justifyContent = node.style.justifyContent;
|
||||
|
||||
float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]));
|
||||
float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]));
|
||||
float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
|
||||
float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));
|
||||
|
||||
boolean isMainDimDefined = !float.IsNaN(node.layout.dimensions[dim[mainAxis]]);
|
||||
boolean isCrossDimDefined = !float.IsNaN(node.layout.dimensions[dim[crossAxis]]);
|
||||
boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE);
|
||||
|
||||
int i;
|
||||
int ii;
|
||||
CSSNode child;
|
||||
int axis;
|
||||
|
||||
CSSNode firstAbsoluteChild = null;
|
||||
CSSNode currentAbsoluteChild = null;
|
||||
|
||||
float definedMainDim = CSSConstants.Undefined;
|
||||
if (isMainDimDefined) {
|
||||
definedMainDim = node.layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
|
||||
}
|
||||
|
||||
// We want to execute the next two loops one per line with flex-wrap
|
||||
int startLine = 0;
|
||||
int endLine = 0;
|
||||
// int nextOffset = 0;
|
||||
int alreadyComputedNextLayout = 0;
|
||||
// We aggregate the total dimensions of the container in those two variables
|
||||
float linesCrossDim = 0;
|
||||
float linesMainDim = 0;
|
||||
int linesCount = 0;
|
||||
while (endLine < childCount) {
|
||||
// <Loop A> Layout non flexible children and count children by type
|
||||
|
||||
// mainContentDim is accumulation of the dimensions and margin of all the
|
||||
// non flexible children. This will be used in order to either set the
|
||||
// dimensions of the node if none already exist, or to compute the
|
||||
// remaining space left for the flexible children.
|
||||
float mainContentDim = 0;
|
||||
|
||||
// There are three kind of children, non flexible, flexible and absolute.
|
||||
// We need to know how many there are in order to distribute the space.
|
||||
int flexibleChildrenCount = 0;
|
||||
float totalFlexible = 0;
|
||||
int nonFlexibleChildrenCount = 0;
|
||||
|
||||
// Use the line loop to position children in the main axis for as long
|
||||
// as they are using a simple stacking behaviour. Children that are
|
||||
// immediately stacked in the initial loop will not be touched again
|
||||
// in <Loop C>.
|
||||
boolean isSimpleStackMain =
|
||||
(isMainDimDefined && justifyContent == CSSJustify.FlexStart) ||
|
||||
(!isMainDimDefined && justifyContent != CSSJustify.Center);
|
||||
int firstComplexMain = (isSimpleStackMain ? childCount : startLine);
|
||||
|
||||
// Use the initial line loop to position children in the cross axis for
|
||||
// as long as they are relatively positioned with alignment STRETCH or
|
||||
// FLEX_START. Children that are immediately stacked in the initial loop
|
||||
// will not be touched again in <Loop D>.
|
||||
boolean isSimpleStackCross = true;
|
||||
int firstComplexCross = childCount;
|
||||
|
||||
CSSNode firstFlexChild = null;
|
||||
CSSNode currentFlexChild = null;
|
||||
|
||||
float mainDim = leadingPaddingAndBorderMain;
|
||||
float crossDim = 0;
|
||||
|
||||
float maxWidth;
|
||||
for (i = startLine; i < childCount; ++i) {
|
||||
child = node.getChildAt(i);
|
||||
child.lineIndex = linesCount;
|
||||
|
||||
child.nextAbsoluteChild = null;
|
||||
child.nextFlexChild = null;
|
||||
|
||||
CSSAlign alignItem = getAlignItem(node, child);
|
||||
|
||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||
// we call the recursive layout pass
|
||||
if (alignItem == CSSAlign.Stretch &&
|
||||
child.style.positionType == CSSPositionType.Relative &&
|
||||
isCrossDimDefined &&
|
||||
!(!float.IsNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) {
|
||||
child.layout.dimensions[dim[crossAxis]] = Math.Max(
|
||||
boundAxis(child, crossAxis, node.layout.dimensions[dim[crossAxis]] -
|
||||
paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
|
||||
// You never want to go smaller than padding
|
||||
((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
|
||||
);
|
||||
} else if (child.style.positionType == CSSPositionType.Absolute) {
|
||||
// Store a private linked list of absolutely positioned children
|
||||
// so that we can efficiently traverse them later.
|
||||
if (firstAbsoluteChild == null) {
|
||||
firstAbsoluteChild = child;
|
||||
}
|
||||
if (currentAbsoluteChild != null) {
|
||||
currentAbsoluteChild.nextAbsoluteChild = child;
|
||||
}
|
||||
currentAbsoluteChild = child;
|
||||
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!float.IsNaN(node.layout.dimensions[dim[axis]]) &&
|
||||
!(!float.IsNaN(child.style.dimensions[dim[axis]]) && child.style.dimensions[dim[axis]] >= 0.0) &&
|
||||
!float.IsNaN(child.style.position[leading[axis]]) &&
|
||||
!float.IsNaN(child.style.position[trailing[axis]])) {
|
||||
child.layout.dimensions[dim[axis]] = Math.Max(
|
||||
boundAxis(child, axis, node.layout.dimensions[dim[axis]] -
|
||||
((node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) -
|
||||
(child.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
|
||||
(float.IsNaN(child.style.position[leading[axis]]) ? 0 : child.style.position[leading[axis]]) -
|
||||
(float.IsNaN(child.style.position[trailing[axis]]) ? 0 : child.style.position[trailing[axis]])),
|
||||
// You never want to go smaller than padding
|
||||
((child.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (child.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + child.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float nextContentDim = 0;
|
||||
|
||||
// It only makes sense to consider a child flexible if we have a computed
|
||||
// dimension for the node.
|
||||
if (isMainDimDefined && (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0)) {
|
||||
flexibleChildrenCount++;
|
||||
totalFlexible += child.style.flex;
|
||||
|
||||
// Store a private linked list of flexible children so that we can
|
||||
// efficiently traverse them later.
|
||||
if (firstFlexChild == null) {
|
||||
firstFlexChild = child;
|
||||
}
|
||||
if (currentFlexChild != null) {
|
||||
currentFlexChild.nextFlexChild = child;
|
||||
}
|
||||
currentFlexChild = child;
|
||||
|
||||
// Even if we don't know its exact size yet, we already know the padding,
|
||||
// border and margin. We'll use this partial information, which represents
|
||||
// the smallest possible size for the child, to compute the remaining
|
||||
// available space.
|
||||
nextContentDim = ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) +
|
||||
(child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||
|
||||
} else {
|
||||
maxWidth = CSSConstants.Undefined;
|
||||
if (!isMainRowDirection) {
|
||||
if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
|
||||
maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
} else {
|
||||
maxWidth = parentMaxWidth -
|
||||
(node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
}
|
||||
|
||||
// This is the main recursive call. We layout non flexible children.
|
||||
if (alreadyComputedNextLayout == 0) {
|
||||
layoutNode(layoutContext, child, maxWidth, direction);
|
||||
}
|
||||
|
||||
// Absolute positioned elements do not take part of the layout, so we
|
||||
// don't use them to compute mainContentDim
|
||||
if (child.style.positionType == CSSPositionType.Relative) {
|
||||
nonFlexibleChildrenCount++;
|
||||
// At this point we know the final size and margin of the element.
|
||||
nextContentDim = (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||
}
|
||||
}
|
||||
|
||||
// The element we are about to add would make us go to the next line
|
||||
if (isNodeFlexWrap &&
|
||||
isMainDimDefined &&
|
||||
mainContentDim + nextContentDim > definedMainDim &&
|
||||
// If there's only one element, then it's bigger than the content
|
||||
// and needs its own line
|
||||
i != startLine) {
|
||||
nonFlexibleChildrenCount--;
|
||||
alreadyComputedNextLayout = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable simple stacking in the main axis for the current line as
|
||||
// we found a non-trivial child. The remaining children will be laid out
|
||||
// in <Loop C>.
|
||||
if (isSimpleStackMain &&
|
||||
(child.style.positionType != CSSPositionType.Relative || (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0))) {
|
||||
isSimpleStackMain = false;
|
||||
firstComplexMain = i;
|
||||
}
|
||||
|
||||
// Disable simple stacking in the cross axis for the current line as
|
||||
// we found a non-trivial child. The remaining children will be laid out
|
||||
// in <Loop D>.
|
||||
if (isSimpleStackCross &&
|
||||
(child.style.positionType != CSSPositionType.Relative ||
|
||||
(alignItem != CSSAlign.Stretch && alignItem != CSSAlign.FlexStart) ||
|
||||
float.IsNaN(child.layout.dimensions[dim[crossAxis]]))) {
|
||||
isSimpleStackCross = false;
|
||||
firstComplexCross = i;
|
||||
}
|
||||
|
||||
if (isSimpleStackMain) {
|
||||
child.layout.position[pos[mainAxis]] += mainDim;
|
||||
if (isMainDimDefined) {
|
||||
child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
|
||||
}
|
||||
|
||||
mainDim += (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||
crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
|
||||
}
|
||||
|
||||
if (isSimpleStackCross) {
|
||||
child.layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
|
||||
if (isCrossDimDefined) {
|
||||
child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
|
||||
}
|
||||
}
|
||||
|
||||
alreadyComputedNextLayout = 0;
|
||||
mainContentDim += nextContentDim;
|
||||
endLine = i + 1;
|
||||
}
|
||||
|
||||
// <Loop B> Layout flexible children and allocate empty space
|
||||
|
||||
// In order to position the elements in the main axis, we have two
|
||||
// controls. The space between the beginning and the first element
|
||||
// and the space between each two elements.
|
||||
float leadingMainDim = 0;
|
||||
float betweenMainDim = 0;
|
||||
|
||||
// The remaining available space that needs to be allocated
|
||||
float remainingMainDim = 0;
|
||||
if (isMainDimDefined) {
|
||||
remainingMainDim = definedMainDim - mainContentDim;
|
||||
} else {
|
||||
remainingMainDim = Math.Max(mainContentDim, 0) - mainContentDim;
|
||||
}
|
||||
|
||||
// If there are flexible children in the mix, they are going to fill the
|
||||
// remaining space
|
||||
if (flexibleChildrenCount != 0) {
|
||||
float flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
float baseMainDim;
|
||||
float boundMainDim;
|
||||
|
||||
// If the flex share of remaining space doesn't meet min/max bounds,
|
||||
// remove this child from flex calculations.
|
||||
currentFlexChild = firstFlexChild;
|
||||
while (currentFlexChild != null) {
|
||||
baseMainDim = flexibleMainDim * currentFlexChild.style.flex +
|
||||
((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
|
||||
boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
|
||||
|
||||
if (baseMainDim != boundMainDim) {
|
||||
remainingMainDim -= boundMainDim;
|
||||
totalFlexible -= currentFlexChild.style.flex;
|
||||
}
|
||||
|
||||
currentFlexChild = currentFlexChild.nextFlexChild;
|
||||
}
|
||||
flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
|
||||
// The non flexible children can overflow the container, in this case
|
||||
// we should just assume that there is no space available.
|
||||
if (flexibleMainDim < 0) {
|
||||
flexibleMainDim = 0;
|
||||
}
|
||||
|
||||
currentFlexChild = firstFlexChild;
|
||||
while (currentFlexChild != null) {
|
||||
// At this point we know the final size of the element in the main
|
||||
// dimension
|
||||
currentFlexChild.layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
|
||||
flexibleMainDim * currentFlexChild.style.flex +
|
||||
((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))
|
||||
);
|
||||
|
||||
maxWidth = CSSConstants.Undefined;
|
||||
if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
|
||||
maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
} else if (!isMainRowDirection) {
|
||||
maxWidth = parentMaxWidth -
|
||||
(node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
|
||||
paddingAndBorderAxisResolvedRow;
|
||||
}
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(layoutContext, currentFlexChild, maxWidth, direction);
|
||||
|
||||
child = currentFlexChild;
|
||||
currentFlexChild = currentFlexChild.nextFlexChild;
|
||||
child.nextFlexChild = null;
|
||||
}
|
||||
|
||||
// We use justifyContent to figure out how to allocate the remaining
|
||||
// space available
|
||||
} else if (justifyContent != CSSJustify.FlexStart) {
|
||||
if (justifyContent == CSSJustify.Center) {
|
||||
leadingMainDim = remainingMainDim / 2;
|
||||
} else if (justifyContent == CSSJustify.FlexEnd) {
|
||||
leadingMainDim = remainingMainDim;
|
||||
} else if (justifyContent == CSSJustify.SpaceBetween) {
|
||||
remainingMainDim = Math.Max(remainingMainDim, 0);
|
||||
if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
|
||||
betweenMainDim = remainingMainDim /
|
||||
(flexibleChildrenCount + nonFlexibleChildrenCount - 1);
|
||||
} else {
|
||||
betweenMainDim = 0;
|
||||
}
|
||||
} else if (justifyContent == CSSJustify.SpaceAround) {
|
||||
// Space on the edges is half of the space between elements
|
||||
betweenMainDim = remainingMainDim /
|
||||
(flexibleChildrenCount + nonFlexibleChildrenCount);
|
||||
leadingMainDim = betweenMainDim / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// <Loop C> Position elements in the main axis and compute dimensions
|
||||
|
||||
// At this point, all the children have their dimensions set. We need to
|
||||
// find their position. In order to do that, we accumulate data in
|
||||
// variables that are also useful to compute the total dimensions of the
|
||||
// container!
|
||||
mainDim += leadingMainDim;
|
||||
|
||||
for (i = firstComplexMain; i < endLine; ++i) {
|
||||
child = node.getChildAt(i);
|
||||
|
||||
if (child.style.positionType == CSSPositionType.Absolute &&
|
||||
!float.IsNaN(child.style.position[leading[mainAxis]])) {
|
||||
// In case the child is position absolute and has left/top being
|
||||
// defined, we override the position to whatever the user said
|
||||
// (and margin/border).
|
||||
child.layout.position[pos[mainAxis]] = (float.IsNaN(child.style.position[leading[mainAxis]]) ? 0 : child.style.position[leading[mainAxis]]) +
|
||||
node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
|
||||
child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]);
|
||||
} else {
|
||||
// If the child is position absolute (without top/left) or relative,
|
||||
// we put it at the current accumulated offset.
|
||||
child.layout.position[pos[mainAxis]] += mainDim;
|
||||
|
||||
// Define the trailing position accordingly.
|
||||
if (isMainDimDefined) {
|
||||
child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
|
||||
}
|
||||
|
||||
// Now that we placed the element, we need to update the variables
|
||||
// We only need to do that for relative elements. Absolute elements
|
||||
// do not take part in that phase.
|
||||
if (child.style.positionType == CSSPositionType.Relative) {
|
||||
// The main dimension is the sum of all the elements dimension plus
|
||||
// the spacing.
|
||||
mainDim += betweenMainDim + (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||
// The cross dimension is the max of the elements dimension since there
|
||||
// can only be one element in that cross dimension.
|
||||
crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float containerCrossAxis = node.layout.dimensions[dim[crossAxis]];
|
||||
if (!isCrossDimDefined) {
|
||||
containerCrossAxis = Math.Max(
|
||||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
|
||||
paddingAndBorderAxisCross
|
||||
);
|
||||
}
|
||||
|
||||
// <Loop D> Position elements in the cross axis
|
||||
for (i = firstComplexCross; i < endLine; ++i) {
|
||||
child = node.getChildAt(i);
|
||||
|
||||
if (child.style.positionType == CSSPositionType.Absolute &&
|
||||
!float.IsNaN(child.style.position[leading[crossAxis]])) {
|
||||
// In case the child is absolutely positionned and has a
|
||||
// top/left/bottom/right being set, we override all the previously
|
||||
// computed positions to set it correctly.
|
||||
child.layout.position[pos[crossAxis]] = (float.IsNaN(child.style.position[leading[crossAxis]]) ? 0 : child.style.position[leading[crossAxis]]) +
|
||||
node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
|
||||
child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
|
||||
|
||||
} else {
|
||||
float leadingCrossDim = leadingPaddingAndBorderCross;
|
||||
|
||||
// For a relative children, we're either using alignItems (parent) or
|
||||
// alignSelf (child) in order to determine the position in the cross axis
|
||||
if (child.style.positionType == CSSPositionType.Relative) {
|
||||
/*eslint-disable */
|
||||
// This variable is intentionally re-defined as the code is transpiled to a block scope language
|
||||
CSSAlign alignItem = getAlignItem(node, child);
|
||||
/*eslint-enable */
|
||||
if (alignItem == CSSAlign.Stretch) {
|
||||
// You can only stretch if the dimension has not already been set
|
||||
// previously.
|
||||
if (float.IsNaN(child.layout.dimensions[dim[crossAxis]])) {
|
||||
child.layout.dimensions[dim[crossAxis]] = Math.Max(
|
||||
boundAxis(child, crossAxis, containerCrossAxis -
|
||||
paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
|
||||
// You never want to go smaller than padding
|
||||
((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
|
||||
);
|
||||
}
|
||||
} else if (alignItem != CSSAlign.FlexStart) {
|
||||
// The remaining space between the parent dimensions+padding and child
|
||||
// dimensions+margin.
|
||||
float remainingCrossDim = containerCrossAxis -
|
||||
paddingAndBorderAxisCross - (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]));
|
||||
|
||||
if (alignItem == CSSAlign.Center) {
|
||||
leadingCrossDim += remainingCrossDim / 2;
|
||||
} else { // CSSAlign.FlexEnd
|
||||
leadingCrossDim += remainingCrossDim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And we apply the position
|
||||
child.layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
|
||||
|
||||
// Define the trailing position accordingly.
|
||||
if (isCrossDimDefined) {
|
||||
child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linesCrossDim += crossDim;
|
||||
linesMainDim = Math.Max(linesMainDim, mainDim);
|
||||
linesCount += 1;
|
||||
startLine = endLine;
|
||||
}
|
||||
|
||||
// <Loop E>
|
||||
//
|
||||
// Note(prenaux): More than one line, we need to layout the crossAxis
|
||||
// according to alignContent.
|
||||
//
|
||||
// Note that we could probably remove <Loop D> and handle the one line case
|
||||
// here too, but for the moment this is safer since it won't interfere with
|
||||
// previously working code.
|
||||
//
|
||||
// See specs:
|
||||
// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
|
||||
// section 9.4
|
||||
//
|
||||
if (linesCount > 1 && isCrossDimDefined) {
|
||||
float nodeCrossAxisInnerSize = node.layout.dimensions[dim[crossAxis]] -
|
||||
paddingAndBorderAxisCross;
|
||||
float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
|
||||
|
||||
float crossDimLead = 0;
|
||||
float currentLead = leadingPaddingAndBorderCross;
|
||||
|
||||
CSSAlign alignContent = node.style.alignContent;
|
||||
if (alignContent == CSSAlign.FlexEnd) {
|
||||
currentLead += remainingAlignContentDim;
|
||||
} else if (alignContent == CSSAlign.Center) {
|
||||
currentLead += remainingAlignContentDim / 2;
|
||||
} else if (alignContent == CSSAlign.Stretch) {
|
||||
if (nodeCrossAxisInnerSize > linesCrossDim) {
|
||||
crossDimLead = (remainingAlignContentDim / linesCount);
|
||||
}
|
||||
}
|
||||
|
||||
int endIndex = 0;
|
||||
for (i = 0; i < linesCount; ++i) {
|
||||
int startIndex = endIndex;
|
||||
|
||||
// compute the line's height and find the endIndex
|
||||
float lineHeight = 0;
|
||||
for (ii = startIndex; ii < childCount; ++ii) {
|
||||
child = node.getChildAt(ii);
|
||||
if (child.style.positionType != CSSPositionType.Relative) {
|
||||
continue;
|
||||
}
|
||||
if (child.lineIndex != i) {
|
||||
break;
|
||||
}
|
||||
if (!float.IsNaN(child.layout.dimensions[dim[crossAxis]])) {
|
||||
lineHeight = Math.Max(
|
||||
lineHeight,
|
||||
child.layout.dimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))
|
||||
);
|
||||
}
|
||||
}
|
||||
endIndex = ii;
|
||||
lineHeight += crossDimLead;
|
||||
|
||||
for (ii = startIndex; ii < endIndex; ++ii) {
|
||||
child = node.getChildAt(ii);
|
||||
if (child.style.positionType != CSSPositionType.Relative) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CSSAlign alignContentAlignItem = getAlignItem(node, child);
|
||||
if (alignContentAlignItem == CSSAlign.FlexStart) {
|
||||
child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
|
||||
} else if (alignContentAlignItem == CSSAlign.FlexEnd) {
|
||||
child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.dimensions[dim[crossAxis]];
|
||||
} else if (alignContentAlignItem == CSSAlign.Center) {
|
||||
float childHeight = child.layout.dimensions[dim[crossAxis]];
|
||||
child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
|
||||
} else if (alignContentAlignItem == CSSAlign.Stretch) {
|
||||
child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
|
||||
// TODO(prenaux): Correctly set the height of items with undefined
|
||||
// (auto) crossAxis dimension.
|
||||
}
|
||||
}
|
||||
|
||||
currentLead += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
boolean needsMainTrailingPos = false;
|
||||
boolean needsCrossTrailingPos = false;
|
||||
|
||||
// If the user didn't specify a width or height, and it has not been set
|
||||
// by the container, then we set it via the children.
|
||||
if (!isMainDimDefined) {
|
||||
node.layout.dimensions[dim[mainAxis]] = Math.Max(
|
||||
// We're missing the last padding at this point to get the final
|
||||
// dimension
|
||||
boundAxis(node, mainAxis, linesMainDim + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))),
|
||||
// We can never assign a width smaller than the padding and borders
|
||||
paddingAndBorderAxisMain
|
||||
);
|
||||
|
||||
if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||
mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||
needsMainTrailingPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCrossDimDefined) {
|
||||
node.layout.dimensions[dim[crossAxis]] = Math.Max(
|
||||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
|
||||
paddingAndBorderAxisCross
|
||||
);
|
||||
|
||||
if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||
crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||
needsCrossTrailingPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
// <Loop F> Set trailing position if necessary
|
||||
if (needsMainTrailingPos || needsCrossTrailingPos) {
|
||||
for (i = 0; i < childCount; ++i) {
|
||||
child = node.getChildAt(i);
|
||||
|
||||
if (needsMainTrailingPos) {
|
||||
child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
|
||||
}
|
||||
|
||||
if (needsCrossTrailingPos) {
|
||||
child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <Loop G> Calculate dimensions for absolutely positioned elements
|
||||
currentAbsoluteChild = firstAbsoluteChild;
|
||||
while (currentAbsoluteChild != null) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for
|
||||
// the axis are defined (either both left and right or top and bottom).
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
|
||||
if (!float.IsNaN(node.layout.dimensions[dim[axis]]) &&
|
||||
!(!float.IsNaN(currentAbsoluteChild.style.dimensions[dim[axis]]) && currentAbsoluteChild.style.dimensions[dim[axis]] >= 0.0) &&
|
||||
!float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) &&
|
||||
!float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]])) {
|
||||
currentAbsoluteChild.layout.dimensions[dim[axis]] = Math.Max(
|
||||
boundAxis(currentAbsoluteChild, axis, node.layout.dimensions[dim[axis]] -
|
||||
(node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])) -
|
||||
(currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
|
||||
(float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) ? 0 : currentAbsoluteChild.style.position[leading[axis]]) -
|
||||
(float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]])
|
||||
),
|
||||
// You never want to go smaller than padding
|
||||
((currentAbsoluteChild.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (currentAbsoluteChild.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + currentAbsoluteChild.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
|
||||
);
|
||||
}
|
||||
|
||||
if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) &&
|
||||
!!float.IsNaN(currentAbsoluteChild.style.position[leading[axis]])) {
|
||||
currentAbsoluteChild.layout.position[leading[axis]] =
|
||||
node.layout.dimensions[dim[axis]] -
|
||||
currentAbsoluteChild.layout.dimensions[dim[axis]] -
|
||||
(float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]]);
|
||||
}
|
||||
}
|
||||
|
||||
child = currentAbsoluteChild;
|
||||
currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
|
||||
child.nextAbsoluteChild = null;
|
||||
}
|
||||
}
|
||||
/** END_GENERATED **/
|
||||
}
|
||||
}
|
36
src/csharp/Facebook.CSSLayout/MeasureOutput.cs
Normal file
36
src/csharp/Facebook.CSSLayout/MeasureOutput.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
/**
|
||||
* POJO to hold the output of the measure function.
|
||||
*/
|
||||
public struct MeasureOutput
|
||||
{
|
||||
public MeasureOutput(float width, float height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public readonly float Width;
|
||||
public readonly float Height;
|
||||
|
||||
internal float width
|
||||
{
|
||||
get { return Width; }
|
||||
}
|
||||
|
||||
internal float height
|
||||
{
|
||||
get { return Height; }
|
||||
}
|
||||
}
|
||||
}
|
22
src/csharp/Facebook.CSSLayout/NullableAttribute.cs
Executable file
22
src/csharp/Facebook.CSSLayout/NullableAttribute.cs
Executable file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
/**
|
||||
* This is here to preserve the @nullable attribute of the original Java API.
|
||||
*/
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
|
||||
sealed class NullableAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
27
src/csharp/Facebook.CSSLayout/Properties/AssemblyInfo.cs
Executable file
27
src/csharp/Facebook.CSSLayout/Properties/AssemblyInfo.cs
Executable file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyTitle("Facebook.CSSLayout")]
|
||||
[assembly: AssemblyDescription("A subset of CSS's flexbox layout algorithm and box model.")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Facebook")]
|
||||
[assembly: AssemblyProduct("Facebook.CSSLayout")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015 Facebook")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Facebook.CSSLayout.Tests")]
|
234
src/csharp/Facebook.CSSLayout/Spacing.cs
Normal file
234
src/csharp/Facebook.CSSLayout/Spacing.cs
Normal file
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
namespace Facebook.CSSLayout
|
||||
{
|
||||
/**
|
||||
* Class representing CSS spacing (padding, margin, and borders). This is mostly necessary to
|
||||
* properly implement interactions and updates for properties like margin, marginLeft, and
|
||||
* marginHorizontal.
|
||||
*/
|
||||
|
||||
sealed class Spacing
|
||||
{
|
||||
/**
|
||||
* Spacing type that represents the left direction. E.g. {@code marginLeft}.
|
||||
*/
|
||||
internal const int LEFT = (int)CSSSpacingType.Left;
|
||||
/**
|
||||
* Spacing type that represents the top direction. E.g. {@code marginTop}.
|
||||
*/
|
||||
internal const int TOP = (int)CSSSpacingType.Top;
|
||||
/**
|
||||
* Spacing type that represents the right direction. E.g. {@code marginRight}.
|
||||
*/
|
||||
internal const int RIGHT = (int)CSSSpacingType.Right;
|
||||
/**
|
||||
* Spacing type that represents the bottom direction. E.g. {@code marginBottom}.
|
||||
*/
|
||||
internal const int BOTTOM = (int)CSSSpacingType.Bottom;
|
||||
/**
|
||||
* Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}.
|
||||
*/
|
||||
internal const int VERTICAL = (int)CSSSpacingType.Vertical;
|
||||
/**
|
||||
* Spacing type that represents horizontal direction (left and right). E.g.
|
||||
* {@code marginHorizontal}.
|
||||
*/
|
||||
internal const int HORIZONTAL = (int)CSSSpacingType.Horizontal;
|
||||
/**
|
||||
* Spacing type that represents start direction e.g. left in left-to-right, right in right-to-left.
|
||||
*/
|
||||
internal const int START = (int)CSSSpacingType.Start;
|
||||
/**
|
||||
* Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left.
|
||||
*/
|
||||
internal const int END = (int)CSSSpacingType.End;
|
||||
/**
|
||||
* Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}.
|
||||
*/
|
||||
internal const int ALL = (int)CSSSpacingType.All;
|
||||
|
||||
static readonly int[] sFlagsMap = {
|
||||
1, /*LEFT*/
|
||||
2, /*TOP*/
|
||||
4, /*RIGHT*/
|
||||
8, /*BOTTOM*/
|
||||
16, /*VERTICAL*/
|
||||
32, /*HORIZONTAL*/
|
||||
64, /*START*/
|
||||
128, /*END*/
|
||||
256 /*ALL*/
|
||||
};
|
||||
|
||||
float[] mSpacing = newFullSpacingArray();
|
||||
[Nullable] float[] mDefaultSpacing = null;
|
||||
int mValueFlags = 0;
|
||||
bool mHasAliasesSet;
|
||||
|
||||
/**
|
||||
* Set a spacing value.
|
||||
*
|
||||
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM},
|
||||
* {@link #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
|
||||
* @param value the value for this direction
|
||||
* @return {@code true} if the spacing has changed, or {@code false} if the same value was already
|
||||
* set
|
||||
*/
|
||||
|
||||
internal bool set(int spacingType, float value)
|
||||
{
|
||||
if (!FloatUtil.floatsEqual(mSpacing[spacingType], value))
|
||||
{
|
||||
mSpacing[spacingType] = value;
|
||||
|
||||
if (CSSConstants.IsUndefined(value))
|
||||
{
|
||||
mValueFlags &= ~sFlagsMap[spacingType];
|
||||
}
|
||||
else
|
||||
{
|
||||
mValueFlags |= sFlagsMap[spacingType];
|
||||
}
|
||||
|
||||
mHasAliasesSet =
|
||||
(mValueFlags & sFlagsMap[ALL]) != 0 ||
|
||||
(mValueFlags & sFlagsMap[VERTICAL]) != 0 ||
|
||||
(mValueFlags & sFlagsMap[HORIZONTAL]) != 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a default spacing value. This is used as a fallback when no spacing has been set for a
|
||||
* particular direction.
|
||||
*
|
||||
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
|
||||
* @param value the default value for this direction
|
||||
* @return
|
||||
*/
|
||||
|
||||
internal bool setDefault(int spacingType, float value)
|
||||
{
|
||||
if (mDefaultSpacing == null)
|
||||
mDefaultSpacing = newSpacingResultArray();
|
||||
|
||||
if (!FloatUtil.floatsEqual(mDefaultSpacing[spacingType], value))
|
||||
{
|
||||
mDefaultSpacing[spacingType] = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the spacing for a direction. This takes into account any default values that have been set.
|
||||
*
|
||||
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
|
||||
*/
|
||||
|
||||
internal float get(int spacingType)
|
||||
{
|
||||
float defaultValue =
|
||||
(mDefaultSpacing != null)
|
||||
? mDefaultSpacing[spacingType]
|
||||
: (spacingType == START || spacingType == END ? CSSConstants.Undefined : 0);
|
||||
|
||||
if (mValueFlags == 0)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if ((mValueFlags & sFlagsMap[spacingType]) != 0)
|
||||
{
|
||||
return mSpacing[spacingType];
|
||||
}
|
||||
|
||||
if (mHasAliasesSet)
|
||||
{
|
||||
int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL;
|
||||
if ((mValueFlags & sFlagsMap[secondType]) != 0)
|
||||
{
|
||||
return mSpacing[secondType];
|
||||
}
|
||||
else if ((mValueFlags & sFlagsMap[ALL]) != 0)
|
||||
{
|
||||
return mSpacing[ALL];
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw value (that was set using {@link #set(int, float)}), without taking into account
|
||||
* any default values.
|
||||
*
|
||||
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM},
|
||||
* {@link #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
|
||||
*/
|
||||
|
||||
internal float getRaw(int spacingType)
|
||||
{
|
||||
return mSpacing[spacingType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get start value and fallback to given type if not defined. This is used privately
|
||||
* by the layout engine as a more efficient way to fetch direction-aware values by
|
||||
* avoid extra method invocations.
|
||||
*/
|
||||
internal float getWithFallback(int spacingType, int fallbackType)
|
||||
{
|
||||
return
|
||||
(mValueFlags & sFlagsMap[spacingType]) != 0
|
||||
? mSpacing[spacingType]
|
||||
: get(fallbackType);
|
||||
}
|
||||
|
||||
static float[] newFullSpacingArray()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined
|
||||
};
|
||||
}
|
||||
|
||||
static float[] newSpacingResultArray()
|
||||
{
|
||||
return newSpacingResultArray(0);
|
||||
}
|
||||
|
||||
static float[] newSpacingResultArray(float defaultValue)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
defaultValue,
|
||||
defaultValue,
|
||||
defaultValue,
|
||||
defaultValue,
|
||||
defaultValue,
|
||||
defaultValue,
|
||||
CSSConstants.Undefined,
|
||||
CSSConstants.Undefined,
|
||||
defaultValue
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
38
src/csharp/Makefile
Normal file
38
src/csharp/Makefile
Normal file
@@ -0,0 +1,38 @@
|
||||
MSB=msbuild.exe /m /verbosity:m /nologo
|
||||
NUGET=nuget.exe
|
||||
NUNITC=nunit-console.exe
|
||||
|
||||
VER=1.0.0
|
||||
NAME=Facebook.CSSLayout
|
||||
|
||||
.PHONY: all
|
||||
all: test
|
||||
|
||||
.PHONY: distribute
|
||||
distribute: package release-package
|
||||
|
||||
.PHONY: package
|
||||
package: conf=Release
|
||||
package: build
|
||||
cd ${NAME} && ${NUGET} pack ${NAME}.csproj -Version ${VER} -Prop Configuration=${conf}
|
||||
|
||||
.PHONY: release-package
|
||||
release-package:
|
||||
cd ${NAME} && nuget push ${NAME}.${VER}.nupkg
|
||||
|
||||
.PHONY: test
|
||||
test: build-debug
|
||||
cd ${NAME}.Tests/bin/Debug && ${NUNITC} Facebook.CSSLayout.Tests.dll
|
||||
|
||||
.PHONY: build-debug
|
||||
build-debug: conf=Debug
|
||||
build-debug: build
|
||||
|
||||
.PHONY: build-release
|
||||
build-release: conf=Release
|
||||
build-release: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
${MSB} ${NAME}.sln /p:Configuration=${conf} /t:"Facebook_CSSLayout:Rebuild;Facebook_CSSLayout_Tests:Rebuild"
|
||||
|
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file uses the following specific UMD implementation:
|
||||
// https://github.com/umdjs/umd/blob/master/returnExports.js
|
||||
(function (root, factory) {
|
||||
(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory);
|
||||
@@ -16,11 +16,14 @@
|
||||
// Browser globals (root is window)
|
||||
root.computeLayout = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
// @@include('./Layout.js')
|
||||
}(this, function() {
|
||||
// @@include('./Layout.js')
|
||||
|
||||
return function(node) {
|
||||
computeLayout.fillNodes(node);
|
||||
computeLayout.computeLayout(node);
|
||||
return function(node) {
|
||||
/*eslint-disable */
|
||||
// disabling ESLint because this code relies on the above include
|
||||
computeLayout.fillNodes(node);
|
||||
computeLayout.computeLayout(node);
|
||||
/*eslint-enable */
|
||||
};
|
||||
}));
|
||||
|
@@ -8,49 +8,50 @@
|
||||
*/
|
||||
package com.facebook.csslayout;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
|
||||
*/
|
||||
public class CSSLayout {
|
||||
public static final int POSITION_LEFT = 0;
|
||||
public static final int POSITION_TOP = 1;
|
||||
public static final int POSITION_RIGHT = 2;
|
||||
public static final int POSITION_BOTTOM = 3;
|
||||
|
||||
public float top;
|
||||
public float left;
|
||||
public float right;
|
||||
public float bottom;
|
||||
public float width = CSSConstants.UNDEFINED;
|
||||
public float height = CSSConstants.UNDEFINED;
|
||||
public static final int DIMENSION_WIDTH = 0;
|
||||
public static final int DIMENSION_HEIGHT = 1;
|
||||
|
||||
public float[] position = new float[4];
|
||||
public float[] dimensions = new float[2];
|
||||
public CSSDirection direction = CSSDirection.LTR;
|
||||
|
||||
/**
|
||||
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
|
||||
*/
|
||||
public void resetResult() {
|
||||
left = 0;
|
||||
top = 0;
|
||||
right = 0;
|
||||
bottom = 0;
|
||||
width = CSSConstants.UNDEFINED;
|
||||
height = CSSConstants.UNDEFINED;
|
||||
Arrays.fill(position, 0);
|
||||
Arrays.fill(dimensions, CSSConstants.UNDEFINED);
|
||||
direction = CSSDirection.LTR;
|
||||
}
|
||||
|
||||
public void copy(CSSLayout layout) {
|
||||
left = layout.left;
|
||||
top = layout.top;
|
||||
right = layout.right;
|
||||
bottom = layout.bottom;
|
||||
width = layout.width;
|
||||
height = layout.height;
|
||||
position[POSITION_LEFT] = layout.position[POSITION_LEFT];
|
||||
position[POSITION_TOP] = layout.position[POSITION_TOP];
|
||||
position[POSITION_RIGHT] = layout.position[POSITION_RIGHT];
|
||||
position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM];
|
||||
dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH];
|
||||
dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT];
|
||||
direction = layout.direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "layout: {" +
|
||||
"left: " + left + ", " +
|
||||
"top: " + top + ", " +
|
||||
"width: " + width + ", " +
|
||||
"height: " + height + ", " +
|
||||
"left: " + position[POSITION_LEFT] + ", " +
|
||||
"top: " + position[POSITION_TOP] + ", " +
|
||||
"width: " + dimensions[DIMENSION_WIDTH] + ", " +
|
||||
"height: " + dimensions[DIMENSION_HEIGHT] + ", " +
|
||||
"direction: " + direction +
|
||||
"}";
|
||||
}
|
||||
|
@@ -14,6 +14,13 @@ import java.util.ArrayList;
|
||||
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
|
||||
import static com.facebook.csslayout.CSSLayout.DIMENSION_HEIGHT;
|
||||
import static com.facebook.csslayout.CSSLayout.DIMENSION_WIDTH;
|
||||
import static com.facebook.csslayout.CSSLayout.POSITION_BOTTOM;
|
||||
import static com.facebook.csslayout.CSSLayout.POSITION_LEFT;
|
||||
import static com.facebook.csslayout.CSSLayout.POSITION_RIGHT;
|
||||
import static com.facebook.csslayout.CSSLayout.POSITION_TOP;
|
||||
|
||||
/**
|
||||
* A CSS Node. It has a style object you can manipulate at {@link #style}. After calling
|
||||
* {@link #calculateLayout()}, {@link #layout} will be filled with the results of the layout.
|
||||
@@ -56,6 +63,9 @@ public class CSSNode {
|
||||
|
||||
public int lineIndex = 0;
|
||||
|
||||
/*package*/ CSSNode nextAbsoluteChild;
|
||||
/*package*/ CSSNode nextFlexChild;
|
||||
|
||||
private @Nullable ArrayList<CSSNode> mChildren;
|
||||
private @Nullable CSSNode mParent;
|
||||
private @Nullable MeasureFunction mMeasureFunction = null;
|
||||
@@ -105,7 +115,7 @@ public class CSSNode {
|
||||
}
|
||||
|
||||
public void setMeasureFunction(MeasureFunction measureFunction) {
|
||||
if (!valuesEqual(mMeasureFunction, measureFunction)) {
|
||||
if (mMeasureFunction != measureFunction) {
|
||||
mMeasureFunction = measureFunction;
|
||||
dirty();
|
||||
}
|
||||
@@ -211,62 +221,104 @@ public class CSSNode {
|
||||
return FloatUtil.floatsEqual(f1, f2);
|
||||
}
|
||||
|
||||
protected <T> boolean valuesEqual(@Nullable T o1, @Nullable T o2) {
|
||||
if (o1 == null) {
|
||||
return o2 == null;
|
||||
}
|
||||
return o1.equals(o2);
|
||||
/**
|
||||
* Get this node's direction, as defined in the style.
|
||||
*/
|
||||
public CSSDirection getStyleDirection() {
|
||||
return style.direction;
|
||||
}
|
||||
|
||||
public void setDirection(CSSDirection direction) {
|
||||
if (!valuesEqual(style.direction, direction)) {
|
||||
if (style.direction != direction) {
|
||||
style.direction = direction;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's flex direction, as defined by style.
|
||||
*/
|
||||
public CSSFlexDirection getFlexDirection() {
|
||||
return style.flexDirection;
|
||||
}
|
||||
|
||||
public void setFlexDirection(CSSFlexDirection flexDirection) {
|
||||
if (!valuesEqual(style.flexDirection, flexDirection)) {
|
||||
if (style.flexDirection != flexDirection) {
|
||||
style.flexDirection = flexDirection;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's justify content, as defined by style.
|
||||
*/
|
||||
public CSSJustify getJustifyContent() {
|
||||
return style.justifyContent;
|
||||
}
|
||||
|
||||
public void setJustifyContent(CSSJustify justifyContent) {
|
||||
if (!valuesEqual(style.justifyContent, justifyContent)) {
|
||||
if (style.justifyContent != justifyContent) {
|
||||
style.justifyContent = justifyContent;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's align items, as defined by style.
|
||||
*/
|
||||
public CSSAlign getAlignItems() {
|
||||
return style.alignItems;
|
||||
}
|
||||
|
||||
public void setAlignItems(CSSAlign alignItems) {
|
||||
if (!valuesEqual(style.alignItems, alignItems)) {
|
||||
if (style.alignItems != alignItems) {
|
||||
style.alignItems = alignItems;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's align items, as defined by style.
|
||||
*/
|
||||
public CSSAlign getAlignSelf() {
|
||||
return style.alignSelf;
|
||||
}
|
||||
|
||||
public void setAlignSelf(CSSAlign alignSelf) {
|
||||
if (!valuesEqual(style.alignSelf, alignSelf)) {
|
||||
if (style.alignSelf != alignSelf) {
|
||||
style.alignSelf = alignSelf;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's position type, as defined by style.
|
||||
*/
|
||||
public CSSPositionType getPositionType() {
|
||||
return style.positionType;
|
||||
}
|
||||
|
||||
public void setPositionType(CSSPositionType positionType) {
|
||||
if (!valuesEqual(style.positionType, positionType)) {
|
||||
if (style.positionType != positionType) {
|
||||
style.positionType = positionType;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setWrap(CSSWrap flexWrap) {
|
||||
if (!valuesEqual(style.flexWrap, flexWrap)) {
|
||||
if (style.flexWrap != flexWrap) {
|
||||
style.flexWrap = flexWrap;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's flex, as defined by style.
|
||||
*/
|
||||
public float getFlex() {
|
||||
return style.flex;
|
||||
}
|
||||
|
||||
public void setFlex(float flex) {
|
||||
if (!valuesEqual(style.flex, flex)) {
|
||||
style.flex = flex;
|
||||
@@ -274,112 +326,147 @@ public class CSSNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's margin, as defined by style + default margin.
|
||||
*/
|
||||
public Spacing getMargin() {
|
||||
return style.margin;
|
||||
}
|
||||
|
||||
public void setMargin(int spacingType, float margin) {
|
||||
if (style.margin.set(spacingType, margin)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's padding, as defined by style + default padding.
|
||||
*/
|
||||
public Spacing getPadding() {
|
||||
return style.padding;
|
||||
}
|
||||
|
||||
public void setPadding(int spacingType, float padding) {
|
||||
if (style.padding.set(spacingType, padding)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's border, as defined by style.
|
||||
*/
|
||||
public Spacing getBorder() {
|
||||
return style.border;
|
||||
}
|
||||
|
||||
public void setBorder(int spacingType, float border) {
|
||||
if (style.border.set(spacingType, border)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's position top, as defined by style.
|
||||
*/
|
||||
public float getPositionTop() {
|
||||
return style.position[POSITION_TOP];
|
||||
}
|
||||
|
||||
public void setPositionTop(float positionTop) {
|
||||
if (!valuesEqual(style.positionTop, positionTop)) {
|
||||
style.positionTop = positionTop;
|
||||
if (!valuesEqual(style.position[POSITION_TOP], positionTop)) {
|
||||
style.position[POSITION_TOP] = positionTop;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionBottom(float positionBottom) {
|
||||
if (!valuesEqual(style.positionBottom, positionBottom)) {
|
||||
style.positionBottom = positionBottom;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionLeft(float positionLeft) {
|
||||
if (!valuesEqual(style.positionLeft, positionLeft)) {
|
||||
style.positionLeft = positionLeft;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionRight(float positionRight) {
|
||||
if (!valuesEqual(style.positionRight, positionRight)) {
|
||||
style.positionRight = positionRight;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStyleWidth(float width) {
|
||||
if (!valuesEqual(style.width, width)) {
|
||||
style.width = width;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStyleHeight(float height) {
|
||||
if (!valuesEqual(style.height, height)) {
|
||||
style.height = height;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public float getLayoutX() {
|
||||
return layout.left;
|
||||
}
|
||||
|
||||
public float getLayoutY() {
|
||||
return layout.top;
|
||||
}
|
||||
|
||||
public float getLayoutWidth() {
|
||||
return layout.width;
|
||||
}
|
||||
|
||||
public float getLayoutHeight() {
|
||||
return layout.height;
|
||||
}
|
||||
|
||||
public CSSDirection getLayoutDirection() {
|
||||
return layout.direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's padding, as defined by style + default padding.
|
||||
* Get this node's position bottom, as defined by style.
|
||||
*/
|
||||
public Spacing getStylePadding() {
|
||||
return style.padding;
|
||||
public float getPositionBottom() {
|
||||
return style.position[POSITION_BOTTOM];
|
||||
}
|
||||
|
||||
public void setPositionBottom(float positionBottom) {
|
||||
if (!valuesEqual(style.position[POSITION_BOTTOM], positionBottom)) {
|
||||
style.position[POSITION_BOTTOM] = positionBottom;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's position left, as defined by style.
|
||||
*/
|
||||
public float getPositionLeft() {
|
||||
return style.position[POSITION_LEFT];
|
||||
}
|
||||
|
||||
public void setPositionLeft(float positionLeft) {
|
||||
if (!valuesEqual(style.position[POSITION_LEFT], positionLeft)) {
|
||||
style.position[POSITION_LEFT] = positionLeft;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's position right, as defined by style.
|
||||
*/
|
||||
public float getPositionRight() {
|
||||
return style.position[POSITION_RIGHT];
|
||||
}
|
||||
|
||||
public void setPositionRight(float positionRight) {
|
||||
if (!valuesEqual(style.position[POSITION_RIGHT], positionRight)) {
|
||||
style.position[POSITION_RIGHT] = positionRight;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's width, as defined in the style.
|
||||
*/
|
||||
public float getStyleWidth() {
|
||||
return style.width;
|
||||
return style.dimensions[DIMENSION_WIDTH];
|
||||
}
|
||||
|
||||
public void setStyleWidth(float width) {
|
||||
if (!valuesEqual(style.dimensions[DIMENSION_WIDTH], width)) {
|
||||
style.dimensions[DIMENSION_WIDTH] = width;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's height, as defined in the style.
|
||||
*/
|
||||
public float getStyleHeight() {
|
||||
return style.height;
|
||||
return style.dimensions[DIMENSION_HEIGHT];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's direction, as defined in the style.
|
||||
*/
|
||||
public CSSDirection getStyleDirection() {
|
||||
return style.direction;
|
||||
public void setStyleHeight(float height) {
|
||||
if (!valuesEqual(style.dimensions[DIMENSION_HEIGHT], height)) {
|
||||
style.dimensions[DIMENSION_HEIGHT] = height;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public float getLayoutX() {
|
||||
return layout.position[POSITION_LEFT];
|
||||
}
|
||||
|
||||
public float getLayoutY() {
|
||||
return layout.position[POSITION_TOP];
|
||||
}
|
||||
|
||||
public float getLayoutWidth() {
|
||||
return layout.dimensions[DIMENSION_WIDTH];
|
||||
}
|
||||
|
||||
public float getLayoutHeight() {
|
||||
return layout.dimensions[DIMENSION_HEIGHT];
|
||||
}
|
||||
|
||||
public CSSDirection getLayoutDirection() {
|
||||
return layout.direction;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,4 +477,19 @@ public class CSSNode {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this instance to its default state. This method is meant to be used when
|
||||
* recycling {@link CSSNode} instances.
|
||||
*/
|
||||
public void reset() {
|
||||
if (mParent != null || (mChildren != null && mChildren.size() > 0)) {
|
||||
throw new IllegalStateException("You should not reset an attached CSSNode");
|
||||
}
|
||||
|
||||
style.reset();
|
||||
layout.resetResult();
|
||||
lineIndex = 0;
|
||||
mLayoutState = LayoutState.DIRTY;
|
||||
}
|
||||
}
|
||||
|
@@ -8,36 +8,62 @@
|
||||
*/
|
||||
package com.facebook.csslayout;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The CSS style definition for a {@link CSSNode}.
|
||||
*/
|
||||
public class CSSStyle {
|
||||
|
||||
public CSSDirection direction = CSSDirection.INHERIT;
|
||||
public CSSFlexDirection flexDirection = CSSFlexDirection.COLUMN;
|
||||
public CSSJustify justifyContent = CSSJustify.FLEX_START;
|
||||
public CSSAlign alignContent = CSSAlign.FLEX_START;
|
||||
public CSSAlign alignItems = CSSAlign.STRETCH;
|
||||
public CSSAlign alignSelf = CSSAlign.AUTO;
|
||||
public CSSPositionType positionType = CSSPositionType.RELATIVE;
|
||||
public CSSWrap flexWrap = CSSWrap.NOWRAP;
|
||||
public CSSDirection direction;
|
||||
public CSSFlexDirection flexDirection;
|
||||
public CSSJustify justifyContent;
|
||||
public CSSAlign alignContent;
|
||||
public CSSAlign alignItems;
|
||||
public CSSAlign alignSelf;
|
||||
public CSSPositionType positionType;
|
||||
public CSSWrap flexWrap;
|
||||
public float flex;
|
||||
|
||||
public Spacing margin = new Spacing();
|
||||
public Spacing padding = new Spacing();
|
||||
public Spacing border = new Spacing();
|
||||
|
||||
public float positionTop = CSSConstants.UNDEFINED;
|
||||
public float positionBottom = CSSConstants.UNDEFINED;
|
||||
public float positionLeft = CSSConstants.UNDEFINED;
|
||||
public float positionRight = CSSConstants.UNDEFINED;
|
||||
|
||||
public float width = CSSConstants.UNDEFINED;
|
||||
public float height = CSSConstants.UNDEFINED;
|
||||
public float[] position = new float[4];
|
||||
public float[] dimensions = new float[2];
|
||||
|
||||
public float minWidth = CSSConstants.UNDEFINED;
|
||||
public float minHeight = CSSConstants.UNDEFINED;
|
||||
|
||||
public float maxWidth = CSSConstants.UNDEFINED;
|
||||
public float maxHeight = CSSConstants.UNDEFINED;
|
||||
|
||||
CSSStyle() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
direction = CSSDirection.INHERIT;
|
||||
flexDirection = CSSFlexDirection.COLUMN;
|
||||
justifyContent = CSSJustify.FLEX_START;
|
||||
alignContent = CSSAlign.FLEX_START;
|
||||
alignItems = CSSAlign.STRETCH;
|
||||
alignSelf = CSSAlign.AUTO;
|
||||
positionType = CSSPositionType.RELATIVE;
|
||||
flexWrap = CSSWrap.NOWRAP;
|
||||
flex = 0f;
|
||||
|
||||
margin.reset();;
|
||||
padding.reset();
|
||||
border.reset();
|
||||
|
||||
Arrays.fill(position, CSSConstants.UNDEFINED);
|
||||
Arrays.fill(dimensions, CSSConstants.UNDEFINED);
|
||||
|
||||
minWidth = CSSConstants.UNDEFINED;
|
||||
minHeight = CSSConstants.UNDEFINED;
|
||||
|
||||
maxWidth = CSSConstants.UNDEFINED;
|
||||
maxHeight = CSSConstants.UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,8 @@ package com.facebook.csslayout;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Class representing CSS spacing (padding, margin, and borders). This is mostly necessary to
|
||||
* properly implement interactions and updates for properties like margin, marginLeft, and
|
||||
@@ -55,8 +57,22 @@ public class Spacing {
|
||||
*/
|
||||
public static final int ALL = 8;
|
||||
|
||||
private static final int[] sFlagsMap = {
|
||||
1, /*LEFT*/
|
||||
2, /*TOP*/
|
||||
4, /*RIGHT*/
|
||||
8, /*BOTTOM*/
|
||||
16, /*VERTICAL*/
|
||||
32, /*HORIZONTAL*/
|
||||
64, /*START*/
|
||||
128, /*END*/
|
||||
256, /*ALL*/
|
||||
};
|
||||
|
||||
private final float[] mSpacing = newFullSpacingArray();
|
||||
@Nullable private float[] mDefaultSpacing = null;
|
||||
private int mValueFlags = 0;
|
||||
private boolean mHasAliasesSet;
|
||||
|
||||
/**
|
||||
* Set a spacing value.
|
||||
@@ -70,6 +86,18 @@ public class Spacing {
|
||||
public boolean set(int spacingType, float value) {
|
||||
if (!FloatUtil.floatsEqual(mSpacing[spacingType], value)) {
|
||||
mSpacing[spacingType] = value;
|
||||
|
||||
if (CSSConstants.isUndefined(value)) {
|
||||
mValueFlags &= ~sFlagsMap[spacingType];
|
||||
} else {
|
||||
mValueFlags |= sFlagsMap[spacingType];
|
||||
}
|
||||
|
||||
mHasAliasesSet =
|
||||
(mValueFlags & sFlagsMap[ALL]) != 0 ||
|
||||
(mValueFlags & sFlagsMap[VERTICAL]) != 0 ||
|
||||
(mValueFlags & sFlagsMap[HORIZONTAL]) != 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -100,18 +128,28 @@ public class Spacing {
|
||||
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
|
||||
*/
|
||||
public float get(int spacingType) {
|
||||
int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL;
|
||||
float defaultValue = spacingType == START || spacingType == END ? CSSConstants.UNDEFINED : 0;
|
||||
return
|
||||
!CSSConstants.isUndefined(mSpacing[spacingType])
|
||||
? mSpacing[spacingType]
|
||||
: !CSSConstants.isUndefined(mSpacing[secondType])
|
||||
? mSpacing[secondType]
|
||||
: !CSSConstants.isUndefined(mSpacing[ALL])
|
||||
? mSpacing[ALL]
|
||||
: mDefaultSpacing != null
|
||||
? mDefaultSpacing[spacingType]
|
||||
: defaultValue;
|
||||
float defaultValue = (mDefaultSpacing != null)
|
||||
? mDefaultSpacing[spacingType]
|
||||
: (spacingType == START || spacingType == END ? CSSConstants.UNDEFINED : 0);
|
||||
|
||||
if (mValueFlags == 0) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if ((mValueFlags & sFlagsMap[spacingType]) != 0) {
|
||||
return mSpacing[spacingType];
|
||||
}
|
||||
|
||||
if (mHasAliasesSet) {
|
||||
int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL;
|
||||
if ((mValueFlags & sFlagsMap[secondType]) != 0) {
|
||||
return mSpacing[secondType];
|
||||
} else if ((mValueFlags & sFlagsMap[ALL]) != 0) {
|
||||
return mSpacing[ALL];
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,6 +163,29 @@ public class Spacing {
|
||||
return mSpacing[spacingType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the spacing instance to its default state. This method is meant to be used when
|
||||
* recycling {@link Spacing} instances.
|
||||
*/
|
||||
void reset() {
|
||||
Arrays.fill(mSpacing, CSSConstants.UNDEFINED);
|
||||
mDefaultSpacing = null;
|
||||
mHasAliasesSet = false;
|
||||
mValueFlags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get start value and fallback to given type if not defined. This is used privately
|
||||
* by the layout engine as a more efficient way to fetch direction-aware values by
|
||||
* avoid extra method invocations.
|
||||
*/
|
||||
float getWithFallback(int spacingType, int fallbackType) {
|
||||
return
|
||||
(mValueFlags & sFlagsMap[spacingType]) != 0
|
||||
? mSpacing[spacingType]
|
||||
: get(fallbackType);
|
||||
}
|
||||
|
||||
private static float[] newFullSpacingArray() {
|
||||
return new float[] {
|
||||
CSSConstants.UNDEFINED,
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,10 @@
|
||||
*/
|
||||
|
||||
var layoutTestUtils = require('./Layout-test-utils.js');
|
||||
var computeLayout = require('./Layout.js').computeLayout;
|
||||
var computeLayout = require('./Layout.js').layoutNodeImpl;
|
||||
var fs = require('fs');
|
||||
var JavaTranspiler = require('./JavaTranspiler.js');
|
||||
var CSharpTranspiler = require('./CSharpTranspiler.js');
|
||||
|
||||
var currentTest = '';
|
||||
var allTests = [];
|
||||
@@ -243,6 +244,7 @@ function printLayout(test) {
|
||||
function transpileAnnotatedJStoC(jsCode) {
|
||||
return jsCode
|
||||
.replace('node.style.measure', 'node.measure')
|
||||
.replace(/null/g, 'NULL')
|
||||
.replace(/\.children\.length/g, '.children_count')
|
||||
.replace(/\.width/g, '.dimensions[CSS_WIDTH]')
|
||||
.replace(/\.height/g, '.dimensions[CSS_HEIGHT]')
|
||||
@@ -251,6 +253,8 @@ function transpileAnnotatedJStoC(jsCode) {
|
||||
.replace(/\.minWidth/g, '.minDimensions[CSS_WIDTH]')
|
||||
.replace(/\.minHeight/g, '.minDimensions[CSS_HEIGHT]')
|
||||
.replace(/\.lineIndex/g, '.line_index')
|
||||
.replace(/\.nextAbsoluteChild/g, '.next_absolute_child')
|
||||
.replace(/\.nextFlexChild/g, '.next_flex_child')
|
||||
.replace(/layout\[dim/g, 'layout.dimensions[dim')
|
||||
.replace(/layout\[pos/g, 'layout.position[pos')
|
||||
.replace(/layout\[leading/g, 'layout.position[leading')
|
||||
@@ -261,6 +265,12 @@ function transpileAnnotatedJStoC(jsCode) {
|
||||
.replace(/node\./g, 'node->')
|
||||
.replace(/child\./g, 'child->')
|
||||
.replace(/parent\./g, 'parent->')
|
||||
.replace(/currentAbsoluteChild\./g, 'currentAbsoluteChild->')
|
||||
.replace(/currentFlexChild\./g, 'currentFlexChild->')
|
||||
.replace(/getPositionType\((.+?)\)/g, '$1->style.position_type')
|
||||
.replace(/getJustifyContent\((.+?)\)/g, '$1->style.justify_content')
|
||||
.replace(/getAlignContent\((.+?)\)/g, '$1->style.align_content')
|
||||
.replace(/var\/\*\(c\)!([^*]+)\*\//g, '$1')
|
||||
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
||||
.replace(/ === /g, ' == ')
|
||||
.replace(/ !== /g, ' != ')
|
||||
@@ -301,3 +311,6 @@ generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayout.toSt
|
||||
generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(computeLayout.toString()));
|
||||
generateFile(__dirname + '/java/tests/com/facebook/csslayout/TestConstants.java', JavaTranspiler.transpileCConstDefs(makeConstDefs()));
|
||||
generateFile(__dirname + '/java/tests/com/facebook/csslayout/LayoutEngineTest.java', JavaTranspiler.transpileCTestsArray(allTestsInC));
|
||||
generateFile(__dirname + '/csharp/Facebook.CSSLayout/LayoutEngine.cs', CSharpTranspiler.transpileLayoutEngine(computeLayout.toString()));
|
||||
generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/TestConstants.cs', CSharpTranspiler.transpileCConstDefs(makeConstDefs()));
|
||||
generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs', CSharpTranspiler.transpileCTestsArray(allTestsInC));
|
||||
|
Reference in New Issue
Block a user