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:
|
||||
@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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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 = [];
|
||||
|
121
src/Layout.c
121
src/Layout.c
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
24
src/Layout.h
24
src/Layout.h
@@ -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);
|
||||
|
||||
|
128
src/Layout.js
128
src/Layout.js
@@ -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
@@ -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.
|
||||
|
@@ -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]);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user