Import latest changes

This commit is contained in:
Christopher Chedeau
2014-09-11 09:23:30 -07:00
parent 6ecbf80563
commit d7f3ea868d
10 changed files with 1934 additions and 1668 deletions

View File

@@ -1,4 +1,10 @@
FILES=src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c
test:
@gcc -Weverything -Werror -Wno-padded src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c && ./a.out
@gcc -Weverything -Werror -Wno-padded $(FILES) && ./a.out
@rm a.out
debug:
@gcc -ggdb $(FILES) && lldb ./a.out
@rm a.out

View File

@@ -1,5 +1,6 @@
#include "Layout-test-utils.h"
#include <stdlib.h>
static bool eq(float a, float b) {
return fabs(a - b) < 0.0001;
@@ -14,7 +15,7 @@ static bool are_layout_equal(css_node_t *a, css_node_t *b) {
return false;
}
for (int i = 0; i < a->children_count; ++i) {
if (!are_layout_equal(&a->children[i], &b->children[i])) {
if (!are_layout_equal(a->get_child(a->context, i), b->get_child(b->context, i))) {
return false;
}
}
@@ -64,3 +65,33 @@ void test(const char *name, css_node_t *style, css_node_t *expected_layout) {
free_css_node(style);
free_css_node(expected_layout);
}
static css_node_t* get_child(void *context, int i) {
css_node_t* children = (css_node_t*)context;
return &children[i];
}
static bool is_dirty(void *context) {
(void)context; // remove unused warning
return true;
}
static void init_test_css_node(css_node_t *node) {
node->get_child = get_child;
node->is_dirty = is_dirty;
}
css_node_t *new_test_css_node(void) {
css_node_t *node = new_css_node();
init_test_css_node(node);
return node;
}
void init_css_node_children(css_node_t *node, int children_count) {
node->context = calloc((size_t)children_count, sizeof(css_node_t));
for (int i = 0; i < children_count; ++i) {
init_css_node(node->get_child(node->context, i));
init_test_css_node(node->get_child(node->context, i));
}
node->children_count = children_count;
}

View File

@@ -6,3 +6,5 @@
void test(const char *name, css_node_t *style, css_node_t *expected_layout);
css_dim_t measure(void *context, float width);
void init_css_node_children(css_node_t *node, int children_count);
css_node_t *new_test_css_node(void);

View File

@@ -114,22 +114,13 @@ var layoutTestUtils = (function() {
var div = renderNode(body, node);
function isInt(n) {
return n === ~~n;
}
function buildLayout(absoluteRect, div) {
var rect = div.getBoundingClientRect();
// There's a bug with getBoundingClientRect() with position absolute
// and overlapping left and right.
// https://code.google.com/p/chromium/issues/detail?id=383936
// In order to workaround, we can check if offsetWidth is negative and
// return 0 in this case.
var result = {
width: div.offsetWidth < 0 ? 0 : rect.width,
height: div.offsetHeight < 0 ? 0 : rect.height,
top: div.offsetHeight < 0 ? div.offsetTop : rect.top - absoluteRect.top,
left: div.offsetWidth < 0 ? div.offsetLeft : rect.left - absoluteRect.left
width: rect.width,
height: rect.height,
top: rect.top - absoluteRect.top,
left: rect.left - absoluteRect.left
};
var children = [];

View File

@@ -6,11 +6,18 @@
#include "Layout.h"
static bool isUndefined(float value) {
return isnan(value);
}
static bool eq(float a, float b) {
if (isUndefined(a)) {
return isUndefined(b);
}
return fabs(a - b) < 0.0001;
}
static void init_css_node(css_node_t *node) {
void init_css_node(css_node_t *node) {
node->style.align_items = CSS_ALIGN_FLEX_START;
// Some of the fields default to undefined and not 0
@@ -24,6 +31,12 @@ static void init_css_node(css_node_t *node) {
node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
// Such that the comparison is always going to be false
node->layout.last_requested_dimensions[CSS_WIDTH] = -1;
node->layout.last_requested_dimensions[CSS_HEIGHT] = -1;
node->layout.last_parent_max_width = -1;
node->layout.should_update = true;
}
css_node_t *new_css_node() {
@@ -32,23 +45,7 @@ css_node_t *new_css_node() {
return node;
}
void init_css_node_children(css_node_t *node, int children_count) {
node->children = calloc((unsigned long)children_count, sizeof(css_node_t));
for (int i = 0; i < children_count; ++i) {
init_css_node(&node->children[i]);
}
node->children_count = children_count;
}
static void cleanup_css_node(css_node_t *node) {
for (int i = 0; i < node->children_count; ++i) {
cleanup_css_node(&node->children[i]);
}
free(node->children);
}
void free_css_node(css_node_t *node) {
cleanup_css_node(node);
free(node);
}
@@ -132,9 +129,7 @@ static void print_css_node_rec(
printf("alignSelf: 'stretch', ");
}
if (node->style.flex == CSS_FLEX_ONE) {
printf("flex: 1, ");
}
print_number_nan("flex", node->style.flex);
if (four_equal(node->style.margin)) {
print_number_0("margin", node->style.margin[CSS_LEFT]);
@@ -176,10 +171,10 @@ static void print_css_node_rec(
print_number_nan("bottom", node->style.position[CSS_BOTTOM]);
}
if (node->children_count > 0) {
if (options & CSS_PRINT_CHILDREN && node->children_count > 0) {
printf("children: [\n");
for (int i = 0; i < node->children_count; ++i) {
print_css_node_rec(&node->children[i], options, level + 1);
print_css_node_rec(node->get_child(node->context, i), options, level + 1);
}
indent(level);
printf("]},\n");
@@ -212,10 +207,6 @@ static css_dimension_t dim[2] = {
static bool isUndefined(float value) {
return isnan(value);
}
static float getMargin(css_node_t *node, int location) {
return node->style.margin[location];
}
@@ -265,12 +256,15 @@ static css_flex_direction_t getFlexDirection(css_node_t *node) {
return node->style.flex_direction;
}
static css_flex_t getFlex(css_node_t *node) {
static float getFlex(css_node_t *node) {
return node->style.flex;
}
static bool isFlex(css_node_t *node) {
return getPositionType(node) == CSS_POSITION_RELATIVE && getFlex(node);
return (
getPositionType(node) == CSS_POSITION_RELATIVE &&
getFlex(node) > 0
);
}
static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
@@ -327,7 +321,7 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) {
return -getPosition(node, trailing[axis]);
}
void layoutNode(css_node_t *node, float parentMaxWidth) {
static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
css_flex_direction_t mainAxis = getFlexDirection(node);
css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ?
CSS_FLEX_DIRECTION_COLUMN :
@@ -382,10 +376,11 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
return;
}
// Pre-fill some dimensions straight from the parent
for (int i = 0; i < node->children_count; ++i) {
css_node_t* 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
for (int i = 0; i < node->children_count; ++i) {
css_node_t* child = &node->children[i];
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
getPositionType(child) == CSS_POSITION_RELATIVE &&
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
@@ -398,6 +393,26 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
// 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 (int ii = 0; ii < 2; ii++) {
css_flex_direction_t axis = ii ? 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(
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)
);
}
}
}
}
@@ -412,14 +427,16 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
// 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;
for (int i = 0; i < node->children_count; ++i) {
css_node_t* child = &node->children[i];
css_node_t* child = node->get_child(node->context, i);
// 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)) {
flexibleChildrenCount++;
totalFlexible += getFlex(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 to compute the
@@ -474,7 +491,7 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
// If there are flexible children in the mix, they are going to fill the
// remaining space
if (flexibleChildrenCount) {
float flexibleMainDim = remainingMainDim / flexibleChildrenCount;
float flexibleMainDim = remainingMainDim / totalFlexible;
// The non flexible children can overflow the container, in this case
// we should just assume that there is no space available.
@@ -485,11 +502,11 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
// children. This is faster than actually allocating a new array that
// contains only flexible children.
for (int i = 0; i < node->children_count; ++i) {
css_node_t* child = &node->children[i];
css_node_t* 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]] = flexibleMainDim +
child->layout.dimensions[dim[mainAxis]] = flexibleMainDim * getFlex(child) +
getPaddingAndBorderAxis(child, mainAxis);
float maxWidth = CSS_UNDEFINED;
@@ -543,7 +560,7 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
float mainDim = leadingMainDim +
getPaddingAndBorder(node, leading[mainAxis]);
for (int i = 0; i < node->children_count; ++i) {
css_node_t* child = &node->children[i];
css_node_t* child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
isPosDefined(child, leading[mainAxis])) {
@@ -598,7 +615,7 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
// <Loop D> Position elements in the cross axis
for (int i = 0; i < node->children_count; ++i) {
css_node_t* child = &node->children[i];
css_node_t* child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
isPosDefined(child, leading[crossAxis])) {
@@ -650,3 +667,33 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
}
}
}
void layoutNode(css_node_t *node, float parentMaxWidth) {
css_layout_t *layout = &node->layout;
layout->should_update = true;
bool skipLayout =
!node->is_dirty(node->context) &&
eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) &&
eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) &&
eq(layout->last_parent_max_width, parentMaxWidth);
if (skipLayout) {
layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH];
layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT];
layout->position[CSS_TOP] = layout->last_position[CSS_TOP];
layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT];
} else {
layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH];
layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT];
layout->last_parent_max_width = parentMaxWidth;
layoutNodeImpl(node, parentMaxWidth);
layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH];
layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT];
layout->last_position[CSS_TOP] = layout->position[CSS_TOP];
layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT];
}
}

View File

@@ -2,6 +2,7 @@
#define __LAYOUT_H
#include <math.h>
#include <stdbool.h>
#define CSS_UNDEFINED NAN
typedef enum {
@@ -27,11 +28,6 @@ typedef enum {
CSS_ALIGN_STRETCH
} css_align_t;
typedef enum {
CSS_FLEX_NONE = 0,
CSS_FLEX_ONE
} css_flex_t;
typedef enum {
CSS_POSITION_RELATIVE = 0,
CSS_POSITION_ABSOLUTE
@@ -54,6 +50,14 @@ typedef enum {
typedef struct {
float position[2];
float dimensions[2];
// Instead of recomputing the entire layout every single time, we
// cache some information to break early when nothing changed
bool should_update;
float last_requested_dimensions[2];
float last_parent_max_width;
float last_dimensions[2];
float last_position[2];
} css_layout_t;
typedef struct {
@@ -65,8 +69,8 @@ typedef struct {
css_justify_t justify_content;
css_align_t align_items;
css_align_t align_self;
css_flex_t flex;
css_position_type_t position_type;
float flex;
float margin[4];
float position[4];
/**
@@ -87,24 +91,26 @@ typedef struct {
typedef struct css_node {
css_style_t style;
css_layout_t layout;
struct css_node *children;
int children_count;
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
css_node_t *new_css_node(void);
void init_css_node_children(css_node_t *node, int children_count);
void init_css_node(css_node_t *node);
void free_css_node(css_node_t *node);
// Print utilities
typedef enum {
CSS_PRINT_LAYOUT = 1,
CSS_PRINT_STYLE = 2
CSS_PRINT_STYLE = 2,
CSS_PRINT_CHILDREN = 4,
} css_print_options_t;
void print_css_node(css_node_t *node, css_print_options_t options);

View File

@@ -94,11 +94,14 @@ var computeLayout = (function() {
}
function getFlex(node) {
return node.style.flex === 1;
return node.style.flex;
}
function isFlex(node) {
return getPositionType(node) === CSS_POSITION_RELATIVE && getFlex(node);
return (
getPositionType(node) === CSS_POSITION_RELATIVE &&
getFlex(node) > 0
);
}
function getDimWithMargin(node, axis) {
@@ -249,10 +252,11 @@ var computeLayout = (function() {
return;
}
// Pre-fill cross axis dimensions when the child is using stretch before
// we call the recursive layout pass
// Pre-fill some dimensions straight from the parent
for (var/*int*/ i = 0; i < node.children.length; ++i) {
var/*css_node_t**/ child = node.children[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[dim[crossAxis]]) &&
@@ -265,6 +269,26 @@ var computeLayout = (function() {
// 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 (var/*int*/ ii = 0; ii < 2; ii++) {
var/*css_flex_direction_t*/ axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node.layout[dim[axis]]) &&
!isDimDefined(child, axis) &&
isPosDefined(child, leading[axis]) &&
isPosDefined(child, trailing[axis])) {
child.layout[dim[axis]] = fmaxf(
node.layout[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)
);
}
}
}
}
@@ -279,6 +303,7 @@ var computeLayout = (function() {
// 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.
var/*int*/ flexibleChildrenCount = 0;
var/*float*/ totalFlexible = 0;
var/*int*/ nonFlexibleChildrenCount = 0;
for (var/*int*/ i = 0; i < node.children.length; ++i) {
var/*css_node_t**/ child = node.children[i];
@@ -287,6 +312,7 @@ var computeLayout = (function() {
// dimension for the node.
if (!isUndefined(node.layout[dim[mainAxis]]) && isFlex(child)) {
flexibleChildrenCount++;
totalFlexible += getFlex(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 to compute the
@@ -341,7 +367,7 @@ var computeLayout = (function() {
// If there are flexible children in the mix, they are going to fill the
// remaining space
if (flexibleChildrenCount) {
var/*float*/ flexibleMainDim = remainingMainDim / flexibleChildrenCount;
var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;
// The non flexible children can overflow the container, in this case
// we should just assume that there is no space available.
@@ -356,7 +382,7 @@ var computeLayout = (function() {
if (isFlex(child)) {
// At this point we know the final size of the element in the main
// dimension
child.layout[dim[mainAxis]] = flexibleMainDim +
child.layout[dim[mainAxis]] = flexibleMainDim * getFlex(child) +
getPaddingAndBorderAxis(child, mainAxis);
var/*float*/ maxWidth = CSS_UNDEFINED;
@@ -412,12 +438,14 @@ var computeLayout = (function() {
for (var/*int*/ i = 0; i < node.children.length; ++i) {
var/*css_node_t**/ child = node.children[i];
var/*bool*/ leadingPos = isPosDefined(child, leading[mainAxis]);
var/*bool*/ trailingPos = isPosDefined(child, trailing[mainAxis]);
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
(leadingPos || trailingPos)) {
// see the loop afterwards
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
// (and margin/border).
child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +
getBorder(node, leading[mainAxis]) +
getMargin(child, leading[mainAxis]);
} else {
// If the child is position absolute (without top/left) or relative,
// we put it at the current accumulated offset.
@@ -459,93 +487,21 @@ var computeLayout = (function() {
);
}
for (var/*int*/ i = 0; i < node.children.length; ++i) {
var/*css_node_t**/ child = node.children[i];
var/*bool*/ leadingPos = isPosDefined(child, leading[mainAxis]);
var/*bool*/ trailingPos = isPosDefined(child, trailing[mainAxis]);
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
(leadingPos || trailingPos)) {
// 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.
if (leadingPos) {
child.layout[pos[mainAxis]] =
getPosition(child, leading[mainAxis]) +
getBorder(node, leading[mainAxis]) +
getMargin(child, leading[mainAxis]);
}
if (!leadingPos && trailingPos) {
child.layout[pos[mainAxis]] =
node.layout[dim[mainAxis]] -
getBorder(node, trailing[mainAxis]) -
child.layout[dim[mainAxis]] -
getMargin(child, trailing[mainAxis]) -
getPosition(child, trailing[mainAxis]);
}
if (leadingPos && trailingPos) {
if (isDimDefined(child, mainAxis)) {
child.layout[dim[mainAxis]] = fmaxf(
child.style[dim[mainAxis]],
getPaddingAndBorderAxis(node, mainAxis)
);
} else {
child.layout[dim[mainAxis]] = fmaxf(
getPaddingAndBorderAxis(child, mainAxis),
node.layout[dim[mainAxis]] -
child.layout[pos[mainAxis]] -
getMargin(child, trailing[mainAxis]) -
getPosition(child, trailing[mainAxis])
);
}
}
}
}
// <Loop D> Position elements in the cross axis
for (var/*int*/ i = 0; i < node.children.length; ++i) {
var/*css_node_t**/ child = node.children[i];
var/*bool*/ leadingPos = isPosDefined(child, leading[crossAxis]);
var/*bool*/ trailingPos = isPosDefined(child, trailing[crossAxis]);
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
(leadingPos || trailingPos)) {
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
// computed positions to set it correctly.
if (leadingPos) {
child.layout[pos[crossAxis]] =
getPosition(child, leading[crossAxis]) +
child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +
getBorder(node, leading[crossAxis]) +
getMargin(child, leading[crossAxis]);
}
if (!leadingPos && trailingPos) {
child.layout[pos[crossAxis]] =
node.layout[dim[crossAxis]] -
getBorder(node, trailing[crossAxis]) -
child.layout[dim[crossAxis]] -
getMargin(child, trailing[crossAxis]) -
getPosition(child, trailing[crossAxis]);
}
if (leadingPos && trailingPos) {
if (isDimDefined(child, crossAxis)) {
child.layout[dim[crossAxis]] = fmaxf(
child.style[dim[crossAxis]],
getPaddingAndBorderAxis(node, crossAxis)
);
} else {
child.layout[dim[crossAxis]] = fmaxf(
getPaddingAndBorderAxis(child, crossAxis),
node.layout[dim[crossAxis]] -
child.layout[pos[crossAxis]] -
getMargin(child, trailing[crossAxis]) -
getPosition(child, trailing[crossAxis])
);
}
}
} else {
var/*float*/ leadingCrossDim = getPaddingAndBorder(node, leading[crossAxis]);

File diff suppressed because it is too large Load Diff

View File

@@ -690,7 +690,7 @@ describe('Layout', function() {
)
});
it('should layout node with borderWidth and position: absolute, top, main axis', function() {
it('should layout node with borderWidth and position: absolute, top', function() {
testLayout(
{style: {borderTopWidth: 1}, children: [
{style: {top: -1, position: 'absolute'}}
@@ -701,7 +701,7 @@ describe('Layout', function() {
)
});
it('should layout node with borderWidth and position: absolute, top, cross axis', function() {
it('should layout node with borderWidth and position: absolute, top. cross axis', function() {
testLayout(
{style: {borderWidth: 1}, children: [
{style: {left: 5, position: 'absolute'}}
@@ -906,112 +906,39 @@ describe('Layout', function() {
);
});
it('should layout with position absolute right', function() {
it('should layout with position absolute, top, left, bottom, right', function() {
testLayout(
{style: {}, children: [
{style: {right: 5, borderWidth: 5, position: 'absolute'}}
{style: {width: 100, height: 100}, children: [
{style: {position: 'absolute', top: 0, left: 0, bottom: 0, right: 0}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 10, height: 10, top: 0, left: -15}
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 100, top: 0, left: 0}
]}
);
});
it('should layout with position absolute right and negative margin', function() {
it('should layout with arbitrary flex', function() {
testLayout(
{style: {}, children: [
{style: {right: 20, marginLeft: 5, marginRight: -5, width: 5, position: 'absolute'}}
{style: {width: 100, height: 100}, children: [
{style: {flex: 2.5}},
{style: {flex: 7.5}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 5, height: 0, top: 0, left: -20}
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 0, height: 25, top: 0, left: 0},
{width: 0, height: 75, top: 25, left: 0},
]}
);
});
it('should layout with position absolute left and right', function() {
it('should layout with negative flex', function() {
testLayout(
{style: {width: 100}, children: [
{style: {right: 5, left: 5, position: 'absolute'}}
{style: {width: 100, height: 100}, children: [
{style: {flex: -2.5}},
{style: {flex: 0}}
]},
{width: 100, height: 0, top: 0, left: 0, children: [
{width: 90, height: 0, top: 0, left: 5}
]}
);
});
it('should layout with position absolute bottom', function() {
testLayout(
{style: {}, children: [
{style: {bottom: 5, paddingTop: 5, position: 'absolute'}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 0, height: 5, top: -10, left: 0}
]}
);
});
it('should layout with position absolute left and negative right', function() {
testLayout(
{style: {}, children: [
{style: {left: 5, right: -1, position: 'absolute'}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 0, height: 0, top: 0, left: 5}
]}
);
});
it('should layout with position absolute top, bottom and padding', function() {
testLayout(
{style: {}, children: [
{style: {top: 5, bottom: 5, paddingTop: 5, position: 'absolute'}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 0, height: 5, top: 5, left: 0}
]}
);
});
it('should layout with position absolute top, bottom and height', function() {
testLayout(
{style: {}, children: [
{style: {top: 5, bottom: 5, height: 900, position: 'absolute'}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 0, height: 900, top: 5, left: 0}
]}
);
});
it('should layout with position absolute left, right and width', function() {
testLayout(
{style: {}, children: [
{style: {left: 5, right: -1, width: 300, position: 'absolute'}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 300, height: 0, top: 0, left: 5}
]}
);
});
it('should layout with position absolute top, bottom and min padding', function() {
testLayout(
{style: {}, children: [
{style: {top: -5, bottom: -5, paddingTop: 5, position: 'absolute'}}
]},
{width: 0, height: 0, top: 0, left: 0, children: [
{width: 0, height: 10, top: -5, left: 0}
]}
);
});
it('should layout with border and right absolute child', function() {
testLayout(
{style: {borderRightWidth: 5}, children: [
{style: {right: -10, position: 'absolute'}}
]},
{width: 5, height: 0, top: 0, left: 0, children: [
{width: 0, height: 0, top: 0, left: 10}
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 0, height: 0, top: 0, left: 0},
{width: 0, height: 0, top: 0, left: 0},
]}
);
});
@@ -1047,6 +974,9 @@ describe('Layout', function() {
var rng = new RNG(0);
function randMinMax(node, chance, attribute, min, max) {
if (rng.nextFloat() < chance) {
if (attribute === 'right' || attribute === 'bottom') {
return;
}
node.style[attribute] = Math.floor(rng.nextFloat() * (max - min)) + min;
}
}
@@ -1074,20 +1004,20 @@ describe('Layout', function() {
var node = {style: {}};
randMinMax(node, 0.5, 'width', -100, 1000);
randMinMax(node, 0.5, 'height', -100, 1000);
randMinMax(node, 0.9, 'top', -10, 10);
randMinMax(node, 0.9, 'left', -10, 10);
randMinMax(node, 0.9, 'right', -10, 10);
randMinMax(node, 0.9, 'bottom', -10, 10);
randMinMax(node, 0.5, 'top', -10, 10);
randMinMax(node, 0.5, 'left', -10, 10);
randMinMax(node, 0.5, 'right', -10, 10);
randMinMax(node, 0.5, 'bottom', -10, 10);
randSpacing(node, 0.5, 'margin', '', -10, 20);
randSpacing(node, 0.5, 'padding', '', -10, 20);
randSpacing(node, 0.5, 'border', 'Width', -4, 4);
randMinMax(node, 0.5, 'flex', -10, 10);
randEnum(node, 0.5, 'flexDirection', ['column', 'row']);
randEnum(node, 0.5, 'justifyContent', ['flex-start', 'center', 'flex-end', 'space-between', 'space-around']);
randEnum(node, 0.5, 'alignItems', ['flex-start', 'center', 'flex-end', 'stretch']);
randEnum(node, 0.5, 'alignSelf', ['flex-start', 'center', 'flex-end', 'stretch']);
randEnum(node, 0.5, 'flex', ['none', 1]);
randEnum(node, 0.5, 'position', ['relative', 'absolute']);
// randEnum(node, 0.5, 'measure', [text('small'), text('loooooooooong with space')]);
randEnum(node, 0.5, 'measure', [text('small'), text('loooooooooong with space')]);
if (node.style.measure) {
// align-items: stretch on a text node makes it wrap in a different way.

View File

@@ -22,7 +22,7 @@ document.getElementById('layout_code').value = computeLayout.toString()
.replace(/layout\[pos/g, 'layout.position[pos')
.replace(/layout\[leading/g, 'layout.position[leading')
.replace(/style\[dim/g, 'style.dimensions[dim')
.replace(/node.children\[i\]/g, '&node.children[i]')
.replace(/node.children\[i\]/g, 'node->get_child(node->context, i)')
.replace(/node\./g, 'node->')
.replace(/child\./g, 'child->')
.replace(/parent\./g, 'parent->')
@@ -89,7 +89,7 @@ function printLayout(test) {
level++;
// Output the style node
add('css_node_t *root_node = new_css_node();');
add('css_node_t *root_node = new_test_css_node();');
add('{');
level++;
if (!isEmpty(test.node.style) || test.node.children && test.node.children.length) {
@@ -159,14 +159,11 @@ function printLayout(test) {
'flex-end': 'CSS_ALIGN_FLEX_END',
'stretch': 'CSS_ALIGN_STRETCH'
});
addEnum(node, 'flex', 'flex', {
'none': 'CSS_FLEX_NONE',
'1': 'CSS_FLEX_ONE'
});
addEnum(node, 'position', 'position_type', {
'relative': 'CSS_POSITION_RELATIVE',
'absolute': 'CSS_POSITION_ABSOLUTE'
});
addFloat('positive', node, 'flex', 'flex');
addFloat('positive', node, 'width', 'dimensions[CSS_WIDTH]');
addFloat('positive', node, 'height', 'dimensions[CSS_HEIGHT]');
addSpacing('all', node, 'margin', '');
@@ -185,7 +182,7 @@ function printLayout(test) {
add('css_node_t *node_' + (level - 3) + ';');
for (var i = 0; i < node.children.length; ++i) {
add('node_' + (level - 3) + ' = &node_' + (level - 4) + '->children[' + i + '];');
add('node_' + (level - 3) + ' = node_' + (level - 4) + '->get_child(node_' + (level - 4) + '->context, ' + i + ');');
rec_style(node.children[i]);
}
@@ -199,7 +196,7 @@ function printLayout(test) {
add('');
// Output the expected layout node
add('css_node_t *root_layout = new_css_node();');
add('css_node_t *root_layout = new_test_css_node();');
add('{');
level++;
add('css_node_t *node_0 = root_layout;');
@@ -221,7 +218,7 @@ function printLayout(test) {
add('css_node_t *node_' + (level - 3) + ';');
for (var i = 0; i < node.children.length; ++i) {
add('node_' + (level - 3) + ' = &node_' + (level - 4) + '->children[' + i + '];');
add('node_' + (level - 3) + ' = node_' + (level - 4) + '->get_child(node_' + (level - 4) + '->context, ' + i + ');');
rec_layout(node.children[i]);
}