From b157ab4a00f333c80281b4e61a21f6e1643cde87 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 25 Mar 2016 22:37:00 +0100 Subject: [PATCH] Reset child position/sizes before calculating layout --- dist/css-layout.h | 22 ++++- dist/css-layout.jar | Bin 14883 -> 15019 bytes src/Layout.c | 11 +++ src/Layout.h | 13 ++- src/__tests__/Layout-test.c | 88 +++++++++++++++++ src/__tests__/Layout-test.js | 26 ++++- .../LayoutEngineTest.cs | 90 ++++++++++++++++++ .../com/facebook/csslayout/LayoutEngine.java | 8 +- .../facebook/csslayout/LayoutEngineTest.java | 90 ++++++++++++++++++ 9 files changed, 334 insertions(+), 14 deletions(-) diff --git a/dist/css-layout.h b/dist/css-layout.h index e7a64287..29c08575 100644 --- a/dist/css-layout.h +++ b/dist/css-layout.h @@ -141,8 +141,8 @@ struct css_node { int children_count; int line_index; - css_node_t* next_absolute_child; - css_node_t* next_flex_child; + css_node_t *next_absolute_child; + css_node_t *next_flex_child; css_dim_t (*measure)(void *context, float width, float height); void (*print)(void *context); @@ -151,7 +151,6 @@ struct css_node { void *context; }; - // Lifecycle of nodes and children css_node_t *new_css_node(void); void init_css_node(css_node_t *node); @@ -165,9 +164,13 @@ typedef enum { } css_print_options_t; void print_css_node(css_node_t *node, css_print_options_t options); +bool isUndefined(float value); + // Function that computes the layout! void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection); -bool isUndefined(float value); + +// Reset the calculated layout values for a given node. You should call this before `layoutNode`. +void resetNodeLayout(css_node_t *node); #endif @@ -1460,6 +1463,10 @@ void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, c layout->last_parent_max_height = parentMaxHeight; layout->last_direction = direction; + for (int i = 0, childCount = node->children_count; i < childCount; i++) { + resetNodeLayout(node->get_child(node->context, i)); + } + layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; @@ -1469,4 +1476,11 @@ void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, c } } +void resetNodeLayout(css_node_t *node) { + node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->layout.position[CSS_LEFT] = 0; + node->layout.position[CSS_TOP] = 0; +} + #endif // CSS_LAYOUT_IMPLEMENTATION \ No newline at end of file diff --git a/dist/css-layout.jar b/dist/css-layout.jar index c9a83c89cf72db72d7f67d6136451e10597a7be5..9a0a701dee6a32bb0ff150cc2d9a2fd462bf6a73 100644 GIT binary patch delta 12048 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 delta 11913 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 U$%IEBDMbJs_Kx?{Qx5q501+C(c>n+a diff --git a/src/Layout.c b/src/Layout.c index 5d112330..5b2d2636 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -1286,6 +1286,10 @@ void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, c layout->last_parent_max_height = parentMaxHeight; layout->last_direction = direction; + for (int i = 0, childCount = node->children_count; i < childCount; i++) { + resetNodeLayout(node->get_child(node->context, i)); + } + layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; @@ -1294,3 +1298,10 @@ void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, c layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT]; } } + +void resetNodeLayout(css_node_t *node) { + node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->layout.position[CSS_LEFT] = 0; + node->layout.position[CSS_TOP] = 0; +} diff --git a/src/Layout.h b/src/Layout.h index 6ad53e04..0a82e6c7 100644 --- a/src/Layout.h +++ b/src/Layout.h @@ -137,8 +137,8 @@ struct css_node { int children_count; int line_index; - css_node_t* next_absolute_child; - css_node_t* next_flex_child; + css_node_t *next_absolute_child; + css_node_t *next_flex_child; css_dim_t (*measure)(void *context, float width, float height); void (*print)(void *context); @@ -147,7 +147,6 @@ struct css_node { void *context; }; - // Lifecycle of nodes and children css_node_t *new_css_node(void); void init_css_node(css_node_t *node); @@ -161,8 +160,12 @@ typedef enum { } css_print_options_t; void print_css_node(css_node_t *node, css_print_options_t options); -// Function that computes the layout! -void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection); bool isUndefined(float value); +// Function that computes the layout! +void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection); + +// Reset the calculated layout values for a given node. You should call this before `layoutNode`. +void resetNodeLayout(css_node_t *node); + #endif diff --git a/src/__tests__/Layout-test.c b/src/__tests__/Layout-test.c index fe31e683..13c449ea 100644 --- a/src/__tests__/Layout-test.c +++ b/src/__tests__/Layout-test.c @@ -7916,6 +7916,94 @@ int main() test("should layout child whose cross axis is undefined and whose alignSelf is stretch", root_node, root_layout); } + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + init_css_node_children(node_0, 2); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 100; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.dimensions[CSS_WIDTH] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; + node_2->style.align_items = CSS_ALIGN_CENTER; + init_css_node_children(node_2, 1); + { + css_node_t *node_3; + node_3 = node_2->get_child(node_2->context, 0); + node_3->style.dimensions[CSS_WIDTH] = 50; + node_3->style.dimensions[CSS_HEIGHT] = 50; + } + } + } + } + + 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] = 100; + init_css_node_children(node_0, 2); + { + 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] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 100; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 50; + init_css_node_children(node_2, 1); + { + css_node_t *node_3; + node_3 = node_2->get_child(node_2->context, 0); + node_3->layout.position[CSS_TOP] = 0; + node_3->layout.position[CSS_LEFT] = 25; + node_3->layout.dimensions[CSS_WIDTH] = 50; + node_3->layout.dimensions[CSS_HEIGHT] = 50; + } + } + } + } + + test("should center items correctly inside a stretched layout", root_node, root_layout); + } + { css_node_t *root_node = new_test_css_node(); { diff --git a/src/__tests__/Layout-test.js b/src/__tests__/Layout-test.js index aed51c52..a70ce673 100755 --- a/src/__tests__/Layout-test.js +++ b/src/__tests__/Layout-test.js @@ -41,7 +41,6 @@ describe('Javascript Only', function() { }); }); - describe('Layout', function() { it('should layout a single node with width and height', function() { testLayout({ @@ -2459,6 +2458,31 @@ describe('Layout', function() { ]} ); }); + + it('should center items correctly inside a stretched layout', function() { + testLayout( + {style: {flexDirection: 'row'}, children: [ + {style: {}, children: [ + {style: {width: 100, height: 100}} + ]}, + {style: {width: 100}, children: [ + {style: {flexDirection: 'column', alignItems: 'center'}, children: [ + {style: {width: 50, height: 50}}, + ]}, + ]}, + ]}, + {width: 200, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 100, top: 0, left: 0} + ]}, + {width: 100, height: 100, top: 0, left: 100, children: [ + {width: 100, height: 50, top: 0, left: 0, children: [ + {width: 50, height: 50, top: 0, left: 25} + ]}, + ]}, + ]} + ); + }); }); describe('Layout alignContent', function() { diff --git a/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs b/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs index 3b81ed53..e787b508 100644 --- a/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs +++ b/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs @@ -8387,6 +8387,96 @@ public class LayoutEngineTest [Test] public void TestCase187() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.flexDirection = CSSFlexDirection.Column; + node_2.style.alignItems = CSSAlign.Center; + addChildren(node_2, 1); + { + TestCSSNode node_3; + node_3 = node_2.getChildAt(0); + node_3.style.dimensions[DIMENSION_WIDTH] = 50; + node_3.style.dimensions[DIMENSION_HEIGHT] = 50; + } + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 200; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 100; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 50; + addChildren(node_2, 1); + { + TestCSSNode node_3; + node_3 = node_2.getChildAt(0); + node_3.layout.position[POSITION_TOP] = 0; + node_3.layout.position[POSITION_LEFT] = 25; + node_3.layout.dimensions[DIMENSION_WIDTH] = 50; + node_3.layout.dimensions[DIMENSION_HEIGHT] = 50; + } + } + } + } + + test("should center items correctly inside a stretched layout", root_node, root_layout); + } + + [Test] + public void TestCase188() { TestCSSNode root_node = new TestCSSNode(); { diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index 7106580b..ea1e66c7 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -204,6 +204,10 @@ public class LayoutEngine { node.lastLayout.parentMaxWidth = parentMaxWidth; node.lastLayout.parentMaxHeight = parentMaxHeight; + for (int i = 0, childCount = node.getChildCount(); i < childCount; i++) { + node.getChildAt(i).layout.resetResult(); + } + layoutNodeImpl(layoutContext, node, parentMaxWidth, parentMaxHeight, parentDirection); node.lastLayout.copy(node.layout); } else { @@ -219,10 +223,6 @@ public class LayoutEngine { float parentMaxWidth, float parentMaxHeight, CSSDirection parentDirection) { - for (int i = 0, childCount = node.getChildCount(); i < childCount; i++) { - node.getChildAt(i).layout.resetResult(); - } - /** START_GENERATED **/ CSSDirection direction = resolveDirection(node, parentDirection); diff --git a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java index 66788c50..f44e7642 100644 --- a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java +++ b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java @@ -8390,6 +8390,96 @@ public class LayoutEngineTest { @Test public void testCase187() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.flexDirection = CSSFlexDirection.COLUMN; + node_2.style.alignItems = CSSAlign.CENTER; + addChildren(node_2, 1); + { + TestCSSNode node_3; + node_3 = node_2.getChildAt(0); + node_3.style.dimensions[DIMENSION_WIDTH] = 50; + node_3.style.dimensions[DIMENSION_HEIGHT] = 50; + } + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 200; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 100; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 50; + addChildren(node_2, 1); + { + TestCSSNode node_3; + node_3 = node_2.getChildAt(0); + node_3.layout.position[POSITION_TOP] = 0; + node_3.layout.position[POSITION_LEFT] = 25; + node_3.layout.dimensions[DIMENSION_WIDTH] = 50; + node_3.layout.dimensions[DIMENSION_HEIGHT] = 50; + } + } + } + } + + test("should center items correctly inside a stretched layout", root_node, root_layout); + } + + @Test + public void testCase188() { TestCSSNode root_node = new TestCSSNode(); {