Import latest changes
This commit is contained in:
8
Makefile
8
Makefile
@@ -1,4 +1,10 @@
|
|||||||
|
|
||||||
|
FILES=src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c
|
||||||
|
|
||||||
test:
|
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
|
@rm a.out
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
#include "Layout-test-utils.h"
|
#include "Layout-test-utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
static bool eq(float a, float b) {
|
static bool eq(float a, float b) {
|
||||||
return fabs(a - b) < 0.0001;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < a->children_count; ++i) {
|
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;
|
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(style);
|
||||||
free_css_node(expected_layout);
|
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;
|
||||||
|
}
|
||||||
|
@@ -6,3 +6,5 @@
|
|||||||
|
|
||||||
void test(const char *name, css_node_t *style, css_node_t *expected_layout);
|
void test(const char *name, css_node_t *style, css_node_t *expected_layout);
|
||||||
css_dim_t measure(void *context, float width);
|
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);
|
||||||
|
@@ -114,22 +114,13 @@ var layoutTestUtils = (function() {
|
|||||||
|
|
||||||
var div = renderNode(body, node);
|
var div = renderNode(body, node);
|
||||||
|
|
||||||
function isInt(n) {
|
|
||||||
return n === ~~n;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildLayout(absoluteRect, div) {
|
function buildLayout(absoluteRect, div) {
|
||||||
var rect = div.getBoundingClientRect();
|
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 = {
|
var result = {
|
||||||
width: div.offsetWidth < 0 ? 0 : rect.width,
|
width: rect.width,
|
||||||
height: div.offsetHeight < 0 ? 0 : rect.height,
|
height: rect.height,
|
||||||
top: div.offsetHeight < 0 ? div.offsetTop : rect.top - absoluteRect.top,
|
top: rect.top - absoluteRect.top,
|
||||||
left: div.offsetWidth < 0 ? div.offsetLeft : rect.left - absoluteRect.left
|
left: rect.left - absoluteRect.left
|
||||||
};
|
};
|
||||||
|
|
||||||
var children = [];
|
var children = [];
|
||||||
|
121
src/Layout.c
121
src/Layout.c
@@ -6,11 +6,18 @@
|
|||||||
|
|
||||||
#include "Layout.h"
|
#include "Layout.h"
|
||||||
|
|
||||||
|
static bool isUndefined(float value) {
|
||||||
|
return isnan(value);
|
||||||
|
}
|
||||||
|
|
||||||
static bool eq(float a, float b) {
|
static bool eq(float a, float b) {
|
||||||
|
if (isUndefined(a)) {
|
||||||
|
return isUndefined(b);
|
||||||
|
}
|
||||||
return fabs(a - b) < 0.0001;
|
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;
|
node->style.align_items = CSS_ALIGN_FLEX_START;
|
||||||
|
|
||||||
// Some of the fields default to undefined and not 0
|
// 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_WIDTH] = CSS_UNDEFINED;
|
||||||
node->layout.dimensions[CSS_HEIGHT] = 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() {
|
css_node_t *new_css_node() {
|
||||||
@@ -32,23 +45,7 @@ css_node_t *new_css_node() {
|
|||||||
return 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) {
|
void free_css_node(css_node_t *node) {
|
||||||
cleanup_css_node(node);
|
|
||||||
free(node);
|
free(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,9 +129,7 @@ static void print_css_node_rec(
|
|||||||
printf("alignSelf: 'stretch', ");
|
printf("alignSelf: 'stretch', ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->style.flex == CSS_FLEX_ONE) {
|
print_number_nan("flex", node->style.flex);
|
||||||
printf("flex: 1, ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (four_equal(node->style.margin)) {
|
if (four_equal(node->style.margin)) {
|
||||||
print_number_0("margin", node->style.margin[CSS_LEFT]);
|
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]);
|
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");
|
printf("children: [\n");
|
||||||
for (int i = 0; i < node->children_count; ++i) {
|
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);
|
indent(level);
|
||||||
printf("]},\n");
|
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) {
|
static float getMargin(css_node_t *node, int location) {
|
||||||
return node->style.margin[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;
|
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;
|
return node->style.flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isFlex(css_node_t *node) {
|
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) {
|
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]);
|
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 mainAxis = getFlexDirection(node);
|
||||||
css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ?
|
css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ?
|
||||||
CSS_FLEX_DIRECTION_COLUMN :
|
CSS_FLEX_DIRECTION_COLUMN :
|
||||||
@@ -382,10 +376,11 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
|
|||||||
return;
|
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
|
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||||
// we call the recursive layout pass
|
// 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 &&
|
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
|
||||||
getPositionType(child) == CSS_POSITION_RELATIVE &&
|
getPositionType(child) == CSS_POSITION_RELATIVE &&
|
||||||
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
|
!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
|
// You never want to go smaller than padding
|
||||||
getPaddingAndBorderAxis(child, crossAxis)
|
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.
|
// 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.
|
// We need to know how many there are in order to distribute the space.
|
||||||
int flexibleChildrenCount = 0;
|
int flexibleChildrenCount = 0;
|
||||||
|
float totalFlexible = 0;
|
||||||
int nonFlexibleChildrenCount = 0;
|
int nonFlexibleChildrenCount = 0;
|
||||||
for (int i = 0; i < node->children_count; ++i) {
|
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
|
// It only makes sense to consider a child flexible if we have a computed
|
||||||
// dimension for the node->
|
// dimension for the node->
|
||||||
if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) {
|
if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) {
|
||||||
flexibleChildrenCount++;
|
flexibleChildrenCount++;
|
||||||
|
totalFlexible += getFlex(child);
|
||||||
|
|
||||||
// Even if we don't know its exact size yet, we already know the padding,
|
// 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
|
// 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
|
// If there are flexible children in the mix, they are going to fill the
|
||||||
// remaining space
|
// remaining space
|
||||||
if (flexibleChildrenCount) {
|
if (flexibleChildrenCount) {
|
||||||
float flexibleMainDim = remainingMainDim / flexibleChildrenCount;
|
float flexibleMainDim = remainingMainDim / totalFlexible;
|
||||||
|
|
||||||
// The non flexible children can overflow the container, in this case
|
// The non flexible children can overflow the container, in this case
|
||||||
// we should just assume that there is no space available.
|
// 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
|
// children. This is faster than actually allocating a new array that
|
||||||
// contains only flexible children.
|
// contains only flexible children.
|
||||||
for (int i = 0; i < node->children_count; ++i) {
|
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)) {
|
if (isFlex(child)) {
|
||||||
// At this point we know the final size of the element in the main
|
// At this point we know the final size of the element in the main
|
||||||
// dimension
|
// dimension
|
||||||
child->layout.dimensions[dim[mainAxis]] = flexibleMainDim +
|
child->layout.dimensions[dim[mainAxis]] = flexibleMainDim * getFlex(child) +
|
||||||
getPaddingAndBorderAxis(child, mainAxis);
|
getPaddingAndBorderAxis(child, mainAxis);
|
||||||
|
|
||||||
float maxWidth = CSS_UNDEFINED;
|
float maxWidth = CSS_UNDEFINED;
|
||||||
@@ -543,7 +560,7 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
|
|||||||
float mainDim = leadingMainDim +
|
float mainDim = leadingMainDim +
|
||||||
getPaddingAndBorder(node, leading[mainAxis]);
|
getPaddingAndBorder(node, leading[mainAxis]);
|
||||||
for (int i = 0; i < node->children_count; ++i) {
|
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 &&
|
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||||
isPosDefined(child, leading[mainAxis])) {
|
isPosDefined(child, leading[mainAxis])) {
|
||||||
@@ -598,7 +615,7 @@ void layoutNode(css_node_t *node, float parentMaxWidth) {
|
|||||||
// <Loop D> Position elements in the cross axis
|
// <Loop D> Position elements in the cross axis
|
||||||
|
|
||||||
for (int i = 0; i < node->children_count; ++i) {
|
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 &&
|
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||||
isPosDefined(child, leading[crossAxis])) {
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
24
src/Layout.h
24
src/Layout.h
@@ -2,6 +2,7 @@
|
|||||||
#define __LAYOUT_H
|
#define __LAYOUT_H
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#define CSS_UNDEFINED NAN
|
#define CSS_UNDEFINED NAN
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -27,11 +28,6 @@ typedef enum {
|
|||||||
CSS_ALIGN_STRETCH
|
CSS_ALIGN_STRETCH
|
||||||
} css_align_t;
|
} css_align_t;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CSS_FLEX_NONE = 0,
|
|
||||||
CSS_FLEX_ONE
|
|
||||||
} css_flex_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CSS_POSITION_RELATIVE = 0,
|
CSS_POSITION_RELATIVE = 0,
|
||||||
CSS_POSITION_ABSOLUTE
|
CSS_POSITION_ABSOLUTE
|
||||||
@@ -54,6 +50,14 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
float position[2];
|
float position[2];
|
||||||
float dimensions[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;
|
} css_layout_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -65,8 +69,8 @@ typedef struct {
|
|||||||
css_justify_t justify_content;
|
css_justify_t justify_content;
|
||||||
css_align_t align_items;
|
css_align_t align_items;
|
||||||
css_align_t align_self;
|
css_align_t align_self;
|
||||||
css_flex_t flex;
|
|
||||||
css_position_type_t position_type;
|
css_position_type_t position_type;
|
||||||
|
float flex;
|
||||||
float margin[4];
|
float margin[4];
|
||||||
float position[4];
|
float position[4];
|
||||||
/**
|
/**
|
||||||
@@ -87,24 +91,26 @@ typedef struct {
|
|||||||
typedef struct css_node {
|
typedef struct css_node {
|
||||||
css_style_t style;
|
css_style_t style;
|
||||||
css_layout_t layout;
|
css_layout_t layout;
|
||||||
struct css_node *children;
|
|
||||||
int children_count;
|
int children_count;
|
||||||
|
|
||||||
css_dim_t (*measure)(void *context, float width);
|
css_dim_t (*measure)(void *context, float width);
|
||||||
void (*print)(void *context);
|
void (*print)(void *context);
|
||||||
|
struct css_node* (*get_child)(void *context, int i);
|
||||||
|
bool (*is_dirty)(void *context);
|
||||||
void *context;
|
void *context;
|
||||||
} css_node_t;
|
} css_node_t;
|
||||||
|
|
||||||
|
|
||||||
// Lifecycle of nodes and children
|
// Lifecycle of nodes and children
|
||||||
css_node_t *new_css_node(void);
|
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);
|
void free_css_node(css_node_t *node);
|
||||||
|
|
||||||
// Print utilities
|
// Print utilities
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CSS_PRINT_LAYOUT = 1,
|
CSS_PRINT_LAYOUT = 1,
|
||||||
CSS_PRINT_STYLE = 2
|
CSS_PRINT_STYLE = 2,
|
||||||
|
CSS_PRINT_CHILDREN = 4,
|
||||||
} css_print_options_t;
|
} css_print_options_t;
|
||||||
void print_css_node(css_node_t *node, css_print_options_t options);
|
void print_css_node(css_node_t *node, css_print_options_t options);
|
||||||
|
|
||||||
|
128
src/Layout.js
128
src/Layout.js
@@ -94,11 +94,14 @@ var computeLayout = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFlex(node) {
|
function getFlex(node) {
|
||||||
return node.style.flex === 1;
|
return node.style.flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFlex(node) {
|
function isFlex(node) {
|
||||||
return getPositionType(node) === CSS_POSITION_RELATIVE && getFlex(node);
|
return (
|
||||||
|
getPositionType(node) === CSS_POSITION_RELATIVE &&
|
||||||
|
getFlex(node) > 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDimWithMargin(node, axis) {
|
function getDimWithMargin(node, axis) {
|
||||||
@@ -249,10 +252,11 @@ var computeLayout = (function() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
// Pre-fill some dimensions straight from the parent
|
||||||
// we call the recursive layout pass
|
|
||||||
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
||||||
var/*css_node_t**/ child = node.children[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 &&
|
if (getAlignItem(node, child) === CSS_ALIGN_STRETCH &&
|
||||||
getPositionType(child) === CSS_POSITION_RELATIVE &&
|
getPositionType(child) === CSS_POSITION_RELATIVE &&
|
||||||
!isUndefined(node.layout[dim[crossAxis]]) &&
|
!isUndefined(node.layout[dim[crossAxis]]) &&
|
||||||
@@ -265,6 +269,26 @@ var computeLayout = (function() {
|
|||||||
// You never want to go smaller than padding
|
// You never want to go smaller than padding
|
||||||
getPaddingAndBorderAxis(child, crossAxis)
|
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.
|
// 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.
|
// We need to know how many there are in order to distribute the space.
|
||||||
var/*int*/ flexibleChildrenCount = 0;
|
var/*int*/ flexibleChildrenCount = 0;
|
||||||
|
var/*float*/ totalFlexible = 0;
|
||||||
var/*int*/ nonFlexibleChildrenCount = 0;
|
var/*int*/ nonFlexibleChildrenCount = 0;
|
||||||
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
||||||
var/*css_node_t**/ child = node.children[i];
|
var/*css_node_t**/ child = node.children[i];
|
||||||
@@ -287,6 +312,7 @@ var computeLayout = (function() {
|
|||||||
// dimension for the node.
|
// dimension for the node.
|
||||||
if (!isUndefined(node.layout[dim[mainAxis]]) && isFlex(child)) {
|
if (!isUndefined(node.layout[dim[mainAxis]]) && isFlex(child)) {
|
||||||
flexibleChildrenCount++;
|
flexibleChildrenCount++;
|
||||||
|
totalFlexible += getFlex(child);
|
||||||
|
|
||||||
// Even if we don't know its exact size yet, we already know the padding,
|
// 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
|
// 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
|
// If there are flexible children in the mix, they are going to fill the
|
||||||
// remaining space
|
// remaining space
|
||||||
if (flexibleChildrenCount) {
|
if (flexibleChildrenCount) {
|
||||||
var/*float*/ flexibleMainDim = remainingMainDim / flexibleChildrenCount;
|
var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;
|
||||||
|
|
||||||
// The non flexible children can overflow the container, in this case
|
// The non flexible children can overflow the container, in this case
|
||||||
// we should just assume that there is no space available.
|
// we should just assume that there is no space available.
|
||||||
@@ -356,7 +382,7 @@ var computeLayout = (function() {
|
|||||||
if (isFlex(child)) {
|
if (isFlex(child)) {
|
||||||
// At this point we know the final size of the element in the main
|
// At this point we know the final size of the element in the main
|
||||||
// dimension
|
// dimension
|
||||||
child.layout[dim[mainAxis]] = flexibleMainDim +
|
child.layout[dim[mainAxis]] = flexibleMainDim * getFlex(child) +
|
||||||
getPaddingAndBorderAxis(child, mainAxis);
|
getPaddingAndBorderAxis(child, mainAxis);
|
||||||
|
|
||||||
var/*float*/ maxWidth = CSS_UNDEFINED;
|
var/*float*/ maxWidth = CSS_UNDEFINED;
|
||||||
@@ -412,12 +438,14 @@ var computeLayout = (function() {
|
|||||||
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
||||||
var/*css_node_t**/ child = node.children[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 &&
|
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
|
||||||
(leadingPos || trailingPos)) {
|
isPosDefined(child, leading[mainAxis])) {
|
||||||
// see the loop afterwards
|
// 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 {
|
} else {
|
||||||
// If the child is position absolute (without top/left) or relative,
|
// If the child is position absolute (without top/left) or relative,
|
||||||
// we put it at the current accumulated offset.
|
// 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
|
// <Loop D> Position elements in the cross axis
|
||||||
|
|
||||||
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
for (var/*int*/ i = 0; i < node.children.length; ++i) {
|
||||||
var/*css_node_t**/ child = node.children[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 &&
|
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
|
||||||
(leadingPos || trailingPos)) {
|
isPosDefined(child, leading[crossAxis])) {
|
||||||
// In case the child is absolutely positionned and has a
|
// In case the child is absolutely positionned and has a
|
||||||
// top/left/bottom/right being set, we override all the previously
|
// top/left/bottom/right being set, we override all the previously
|
||||||
// computed positions to set it correctly.
|
// 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]) +
|
getBorder(node, leading[crossAxis]) +
|
||||||
getMargin(child, 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 {
|
} else {
|
||||||
var/*float*/ leadingCrossDim = getPaddingAndBorder(node, leading[crossAxis]);
|
var/*float*/ leadingCrossDim = getPaddingAndBorder(node, leading[crossAxis]);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -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(
|
testLayout(
|
||||||
{style: {borderTopWidth: 1}, children: [
|
{style: {borderTopWidth: 1}, children: [
|
||||||
{style: {top: -1, position: 'absolute'}}
|
{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(
|
testLayout(
|
||||||
{style: {borderWidth: 1}, children: [
|
{style: {borderWidth: 1}, children: [
|
||||||
{style: {left: 5, position: 'absolute'}}
|
{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(
|
testLayout(
|
||||||
{style: {}, children: [
|
{style: {width: 100, height: 100}, children: [
|
||||||
{style: {right: 5, borderWidth: 5, position: 'absolute'}}
|
{style: {position: 'absolute', top: 0, left: 0, bottom: 0, right: 0}}
|
||||||
]},
|
]},
|
||||||
{width: 0, height: 0, top: 0, left: 0, children: [
|
{width: 100, height: 100, top: 0, left: 0, children: [
|
||||||
{width: 10, height: 10, top: 0, left: -15}
|
{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(
|
testLayout(
|
||||||
{style: {}, children: [
|
{style: {width: 100, height: 100}, children: [
|
||||||
{style: {right: 20, marginLeft: 5, marginRight: -5, width: 5, position: 'absolute'}}
|
{style: {flex: 2.5}},
|
||||||
|
{style: {flex: 7.5}}
|
||||||
]},
|
]},
|
||||||
{width: 0, height: 0, top: 0, left: 0, children: [
|
{width: 100, height: 100, top: 0, left: 0, children: [
|
||||||
{width: 5, height: 0, top: 0, left: -20}
|
{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(
|
testLayout(
|
||||||
{style: {width: 100}, children: [
|
{style: {width: 100, height: 100}, children: [
|
||||||
{style: {right: 5, left: 5, position: 'absolute'}}
|
{style: {flex: -2.5}},
|
||||||
|
{style: {flex: 0}}
|
||||||
]},
|
]},
|
||||||
{width: 100, height: 0, top: 0, left: 0, children: [
|
{width: 100, height: 100, top: 0, left: 0, children: [
|
||||||
{width: 90, height: 0, top: 0, left: 5}
|
{width: 0, height: 0, top: 0, left: 0},
|
||||||
]}
|
{width: 0, height: 0, top: 0, left: 0},
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
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}
|
|
||||||
]}
|
]}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1047,6 +974,9 @@ describe('Layout', function() {
|
|||||||
var rng = new RNG(0);
|
var rng = new RNG(0);
|
||||||
function randMinMax(node, chance, attribute, min, max) {
|
function randMinMax(node, chance, attribute, min, max) {
|
||||||
if (rng.nextFloat() < chance) {
|
if (rng.nextFloat() < chance) {
|
||||||
|
if (attribute === 'right' || attribute === 'bottom') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
node.style[attribute] = Math.floor(rng.nextFloat() * (max - min)) + min;
|
node.style[attribute] = Math.floor(rng.nextFloat() * (max - min)) + min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1074,20 +1004,20 @@ describe('Layout', function() {
|
|||||||
var node = {style: {}};
|
var node = {style: {}};
|
||||||
randMinMax(node, 0.5, 'width', -100, 1000);
|
randMinMax(node, 0.5, 'width', -100, 1000);
|
||||||
randMinMax(node, 0.5, 'height', -100, 1000);
|
randMinMax(node, 0.5, 'height', -100, 1000);
|
||||||
randMinMax(node, 0.9, 'top', -10, 10);
|
randMinMax(node, 0.5, 'top', -10, 10);
|
||||||
randMinMax(node, 0.9, 'left', -10, 10);
|
randMinMax(node, 0.5, 'left', -10, 10);
|
||||||
randMinMax(node, 0.9, 'right', -10, 10);
|
randMinMax(node, 0.5, 'right', -10, 10);
|
||||||
randMinMax(node, 0.9, 'bottom', -10, 10);
|
randMinMax(node, 0.5, 'bottom', -10, 10);
|
||||||
randSpacing(node, 0.5, 'margin', '', -10, 20);
|
randSpacing(node, 0.5, 'margin', '', -10, 20);
|
||||||
randSpacing(node, 0.5, 'padding', '', -10, 20);
|
randSpacing(node, 0.5, 'padding', '', -10, 20);
|
||||||
randSpacing(node, 0.5, 'border', 'Width', -4, 4);
|
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, 'flexDirection', ['column', 'row']);
|
||||||
randEnum(node, 0.5, 'justifyContent', ['flex-start', 'center', 'flex-end', 'space-between', 'space-around']);
|
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, 'alignItems', ['flex-start', 'center', 'flex-end', 'stretch']);
|
||||||
randEnum(node, 0.5, 'alignSelf', ['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, '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) {
|
if (node.style.measure) {
|
||||||
// align-items: stretch on a text node makes it wrap in a different way.
|
// align-items: stretch on a text node makes it wrap in a different way.
|
||||||
|
@@ -22,7 +22,7 @@ document.getElementById('layout_code').value = computeLayout.toString()
|
|||||||
.replace(/layout\[pos/g, 'layout.position[pos')
|
.replace(/layout\[pos/g, 'layout.position[pos')
|
||||||
.replace(/layout\[leading/g, 'layout.position[leading')
|
.replace(/layout\[leading/g, 'layout.position[leading')
|
||||||
.replace(/style\[dim/g, 'style.dimensions[dim')
|
.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(/node\./g, 'node->')
|
||||||
.replace(/child\./g, 'child->')
|
.replace(/child\./g, 'child->')
|
||||||
.replace(/parent\./g, 'parent->')
|
.replace(/parent\./g, 'parent->')
|
||||||
@@ -89,7 +89,7 @@ function printLayout(test) {
|
|||||||
level++;
|
level++;
|
||||||
|
|
||||||
// Output the style node
|
// Output the style node
|
||||||
add('css_node_t *root_node = new_css_node();');
|
add('css_node_t *root_node = new_test_css_node();');
|
||||||
add('{');
|
add('{');
|
||||||
level++;
|
level++;
|
||||||
if (!isEmpty(test.node.style) || test.node.children && test.node.children.length) {
|
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',
|
'flex-end': 'CSS_ALIGN_FLEX_END',
|
||||||
'stretch': 'CSS_ALIGN_STRETCH'
|
'stretch': 'CSS_ALIGN_STRETCH'
|
||||||
});
|
});
|
||||||
addEnum(node, 'flex', 'flex', {
|
|
||||||
'none': 'CSS_FLEX_NONE',
|
|
||||||
'1': 'CSS_FLEX_ONE'
|
|
||||||
});
|
|
||||||
addEnum(node, 'position', 'position_type', {
|
addEnum(node, 'position', 'position_type', {
|
||||||
'relative': 'CSS_POSITION_RELATIVE',
|
'relative': 'CSS_POSITION_RELATIVE',
|
||||||
'absolute': 'CSS_POSITION_ABSOLUTE'
|
'absolute': 'CSS_POSITION_ABSOLUTE'
|
||||||
});
|
});
|
||||||
|
addFloat('positive', node, 'flex', 'flex');
|
||||||
addFloat('positive', node, 'width', 'dimensions[CSS_WIDTH]');
|
addFloat('positive', node, 'width', 'dimensions[CSS_WIDTH]');
|
||||||
addFloat('positive', node, 'height', 'dimensions[CSS_HEIGHT]');
|
addFloat('positive', node, 'height', 'dimensions[CSS_HEIGHT]');
|
||||||
addSpacing('all', node, 'margin', '');
|
addSpacing('all', node, 'margin', '');
|
||||||
@@ -185,7 +182,7 @@ function printLayout(test) {
|
|||||||
add('css_node_t *node_' + (level - 3) + ';');
|
add('css_node_t *node_' + (level - 3) + ';');
|
||||||
|
|
||||||
for (var i = 0; i < node.children.length; ++i) {
|
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]);
|
rec_style(node.children[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +196,7 @@ function printLayout(test) {
|
|||||||
add('');
|
add('');
|
||||||
|
|
||||||
// Output the expected layout node
|
// 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('{');
|
add('{');
|
||||||
level++;
|
level++;
|
||||||
add('css_node_t *node_0 = root_layout;');
|
add('css_node_t *node_0 = root_layout;');
|
||||||
@@ -221,7 +218,7 @@ function printLayout(test) {
|
|||||||
add('css_node_t *node_' + (level - 3) + ';');
|
add('css_node_t *node_' + (level - 3) + ';');
|
||||||
|
|
||||||
for (var i = 0; i < node.children.length; ++i) {
|
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]);
|
rec_layout(node.children[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user