3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,4 @@
|
|||||||
a.out
|
a.out
|
||||||
|
*.class
|
||||||
|
/**/java/out/*
|
||||||
|
/**/.idea/workspace.xml
|
||||||
|
19
Makefile
19
Makefile
@@ -1,15 +1,26 @@
|
|||||||
|
|
||||||
FILES=src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c
|
FILES=src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c
|
||||||
|
|
||||||
all: c test
|
all: c c_test java java_test
|
||||||
|
|
||||||
c:
|
c: transpile_all
|
||||||
@node ./src/transpile.js
|
|
||||||
|
|
||||||
test:
|
c_test: c
|
||||||
@gcc -std=c99 -Werror -Wno-padded $(FILES) -lm && ./a.out
|
@gcc -std=c99 -Werror -Wno-padded $(FILES) -lm && ./a.out
|
||||||
@rm a.out
|
@rm a.out
|
||||||
|
|
||||||
|
java: transpile_all src/java
|
||||||
|
@javac -cp ./lib/junit4.jar -sourcepath ./src/java/src:./src/java/tests src/java/tests/com/facebook/csslayout/*.java
|
||||||
|
|
||||||
|
java_test: java
|
||||||
|
@java -cp ./src/java/src:./src/java/tests:./lib/junit4.jar org.junit.runner.JUnitCore \
|
||||||
|
com.facebook.csslayout.LayoutEngineTest \
|
||||||
|
com.facebook.csslayout.LayoutCachingTest \
|
||||||
|
com.facebook.csslayout.CSSNodeTest
|
||||||
|
|
||||||
|
transpile_all: ./src/transpile.js
|
||||||
|
@node ./src/transpile.js
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
@gcc -ggdb $(FILES) -lm && lldb ./a.out
|
@gcc -ggdb $(FILES) -lm && lldb ./a.out
|
||||||
@rm a.out
|
@rm a.out
|
||||||
|
BIN
lib/junit4.jar
Normal file
BIN
lib/junit4.jar
Normal file
Binary file not shown.
110
src/JavaTranspiler.js
Normal file
110
src/JavaTranspiler.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
function __transpileToJavaCommon(code) {
|
||||||
|
return code
|
||||||
|
.replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED')
|
||||||
|
.replace(/css_flex_direction_t/g, 'CSSFlexDirection')
|
||||||
|
.replace(/CSS_FLEX_DIRECTION_/g, 'CSSFlexDirection.')
|
||||||
|
.replace(/css_align_t/g, 'CSSAlign')
|
||||||
|
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
|
||||||
|
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
|
||||||
|
.replace(/css_justify_t/g, 'CSSJustify')
|
||||||
|
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
|
||||||
|
.replace(/css_dim_t/g, 'MeasureOutput')
|
||||||
|
.replace(/bool/g, 'boolean')
|
||||||
|
.replace(/^(\s+)([^\s]+)\s+\+=/gm, '$1$2 = $2 +') // Expand +=
|
||||||
|
.replace(/leading\[([^\]]+)\]/g, 'getLeading($1)')
|
||||||
|
.replace(/trailing\[([^\]]+)\]/g, 'getTrailing($1)')
|
||||||
|
.replace(/pos\[([^\]]+)\]/g, 'getPos($1)')
|
||||||
|
.replace(/dim\[([^\]]+)\]/g, 'getDim($1)')
|
||||||
|
.replace(/isUndefined/g, 'FloatUtil.isUndefined')
|
||||||
|
|
||||||
|
// Since Java doesn't store its attributes in arrays, we need to use setters/getters to access
|
||||||
|
// the appropriate layout/style fields
|
||||||
|
.replace(
|
||||||
|
/(\w+)\.layout\[((?:getLeading|getPos)\([^\)]+\))\]\s+=\s+([^;]+);/gm,
|
||||||
|
'setLayoutPosition($1, $2, $3);')
|
||||||
|
.replace(/(\w+)\.layout\[((?:getLeading|getPos)\([^\]]+\))\]/g, 'getLayoutPosition($1, $2)')
|
||||||
|
.replace(
|
||||||
|
/(\w+)\.layout\[(getDim\([^\)]+\))\]\s+=\s+([^;]+);/gm,
|
||||||
|
'setLayoutDimension($1, $2, $3);')
|
||||||
|
.replace(/(\w+)\.layout\[(getDim\([^\]]+\))\]/g, 'getLayoutDimension($1, $2)')
|
||||||
|
.replace(/(\w+)\.style\[((?:getLeading|getPos)\([^\]]+\))\]/g, 'getStylePosition($1, $2)')
|
||||||
|
.replace(/(\w+)\.style\[(getDim\([^\]]+\))\]/g, 'getStyleDimension($1, $2)');
|
||||||
|
}
|
||||||
|
|
||||||
|
function __transpileSingleTestToJava(code) {
|
||||||
|
return __transpileToJavaCommon(code)
|
||||||
|
.replace(/new_test_css_node/g, 'new TestCSSNode')
|
||||||
|
.replace( // style.dimensions[CSS_WIDTH] => style.width
|
||||||
|
/(style|layout)\.dimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + '.' + match2.toLowerCase();
|
||||||
|
})
|
||||||
|
.replace( // layout.position[CSS_TOP] => layout.y
|
||||||
|
/layout\.position\[CSS_(TOP|LEFT)\]/g,
|
||||||
|
function (str, match1) {
|
||||||
|
return 'layout.' + (match1 == 'TOP' ? 'y' : 'x');
|
||||||
|
})
|
||||||
|
.replace( // style.position[CSS_TOP] => style.positionTop
|
||||||
|
/style\.(position|margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return 'style.' + match1 + match2[0] + match2.substring(1).toLowerCase();
|
||||||
|
})
|
||||||
|
.replace(/get_child\(.*context\,\s([^\)]+)\)/g, 'getChildAt($1)')
|
||||||
|
.replace(/init_css_node_children/g, 'addChildren')
|
||||||
|
.replace(/css_node_t(\s)\*/g, 'TestCSSNode$1')
|
||||||
|
.replace(/\->/g, '.')
|
||||||
|
.replace(/(\d+\.\d+)/g, '$1f')
|
||||||
|
.replace( // style.flex_direction => style.flexDirection
|
||||||
|
/style\.([^_\s]+)_(\w)(\w+)/g,
|
||||||
|
function (str, match1, match2, match3) {
|
||||||
|
return 'style.' + match1 + match2.toUpperCase() + match3;
|
||||||
|
})
|
||||||
|
.replace(/(\w+)\.measure\s+=\s+.+/, '$1.setMeasureFunction(sTestMeasureFunction);');
|
||||||
|
}
|
||||||
|
|
||||||
|
function indent(code) {
|
||||||
|
return code
|
||||||
|
.split('\n')
|
||||||
|
.map(function(line) { return ' ' + line; })
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
var JavaTranspiler = {
|
||||||
|
transpileLayoutEngine: function(code) {
|
||||||
|
return indent(
|
||||||
|
__transpileToJavaCommon(code)
|
||||||
|
.replace(/function\s+layoutNode.*/, '')
|
||||||
|
.replace('node.style.measure', 'node.measure')
|
||||||
|
.replace(/\.children\.length/g, '.getChildCount()')
|
||||||
|
.replace(/node.children\[i\]/g, 'node.getChildAt(i)')
|
||||||
|
.replace(/fmaxf/g, 'Math.max')
|
||||||
|
.replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages
|
||||||
|
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
||||||
|
.replace(/ === /g, ' == ')
|
||||||
|
.replace(/\n /g, '\n')
|
||||||
|
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
|
||||||
|
.replace(/css_node_t\*/g, 'CSSNode'));
|
||||||
|
},
|
||||||
|
|
||||||
|
transpileCConstDefs: function(cConstDefs) {
|
||||||
|
return indent(
|
||||||
|
cConstDefs
|
||||||
|
.replace(/#define\s+(\w+)\s+(\"[^\"]+\")/g, 'public static final String $1 = $2;')
|
||||||
|
.replace(/#define\s+(\w+)\s+(.+)/g, 'public static final float $1 = $2f;'));
|
||||||
|
},
|
||||||
|
|
||||||
|
transpileCTestsArray: function(allTestsInC) {
|
||||||
|
var allTestsInJava = [];
|
||||||
|
for (var i = 0; i < allTestsInC.length; i++) {
|
||||||
|
allTestsInJava[i] =
|
||||||
|
" @Test\n" +
|
||||||
|
" public void testCase" + i + "()\n" +
|
||||||
|
__transpileSingleTestToJava(allTestsInC[i]);
|
||||||
|
}
|
||||||
|
return allTestsInJava.join('\n\n');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports = JavaTranspiler;
|
||||||
|
}
|
@@ -398,7 +398,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||||
// left and right or top and bottom).
|
// left and right or top and bottom).
|
||||||
for (int ii = 0; ii < 2; ii++) {
|
for (int ii = 0; ii < 2; ii++) {
|
||||||
css_flex_direction_t axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||||
!isDimDefined(child, axis) &&
|
!isDimDefined(child, axis) &&
|
||||||
isPosDefined(child, leading[axis]) &&
|
isPosDefined(child, leading[axis]) &&
|
||||||
@@ -491,7 +491,7 @@ static void layoutNodeImpl(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 != 0) {
|
||||||
float flexibleMainDim = remainingMainDim / totalFlexible;
|
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
|
||||||
@@ -675,7 +675,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||||
// left and right or top and bottom).
|
// left and right or top and bottom).
|
||||||
for (int ii = 0; ii < 2; ii++) {
|
for (int ii = 0; ii < 2; ii++) {
|
||||||
css_flex_direction_t axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||||
!isDimDefined(child, axis) &&
|
!isDimDefined(child, axis) &&
|
||||||
isPosDefined(child, leading[axis]) &&
|
isPosDefined(child, leading[axis]) &&
|
||||||
@@ -692,7 +692,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int ii = 0; ii < 2; ii++) {
|
for (int ii = 0; ii < 2; ii++) {
|
||||||
css_flex_direction_t axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
if (isPosDefined(child, trailing[axis]) &&
|
if (isPosDefined(child, trailing[axis]) &&
|
||||||
!isPosDefined(child, leading[axis])) {
|
!isPosDefined(child, leading[axis])) {
|
||||||
child->layout.position[leading[axis]] =
|
child->layout.position[leading[axis]] =
|
||||||
|
@@ -237,7 +237,7 @@ var computeLayout = (function() {
|
|||||||
// Let's not measure the text if we already know both dimensions
|
// Let's not measure the text if we already know both dimensions
|
||||||
if (isRowUndefined || isColumnUndefined) {
|
if (isRowUndefined || isColumnUndefined) {
|
||||||
var/*css_dim_t*/ measure_dim = node.style.measure(
|
var/*css_dim_t*/ measure_dim = node.style.measure(
|
||||||
/*!node->context,*/
|
/*(c)!node->context,*/
|
||||||
width
|
width
|
||||||
);
|
);
|
||||||
if (isRowUndefined) {
|
if (isRowUndefined) {
|
||||||
@@ -273,7 +273,7 @@ var computeLayout = (function() {
|
|||||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||||
// left and right or top and bottom).
|
// left and right or top and bottom).
|
||||||
for (var/*int*/ ii = 0; ii < 2; ii++) {
|
for (var/*int*/ ii = 0; ii < 2; ii++) {
|
||||||
var/*css_flex_direction_t*/ axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
var/*css_flex_direction_t*/ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
if (!isUndefined(node.layout[dim[axis]]) &&
|
if (!isUndefined(node.layout[dim[axis]]) &&
|
||||||
!isDimDefined(child, axis) &&
|
!isDimDefined(child, axis) &&
|
||||||
isPosDefined(child, leading[axis]) &&
|
isPosDefined(child, leading[axis]) &&
|
||||||
@@ -366,7 +366,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 != 0) {
|
||||||
var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;
|
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
|
||||||
@@ -550,7 +550,7 @@ var computeLayout = (function() {
|
|||||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||||
// left and right or top and bottom).
|
// left and right or top and bottom).
|
||||||
for (var/*int*/ ii = 0; ii < 2; ii++) {
|
for (var/*int*/ ii = 0; ii < 2; ii++) {
|
||||||
var/*css_flex_direction_t*/ axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
var/*css_flex_direction_t*/ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
if (!isUndefined(node.layout[dim[axis]]) &&
|
if (!isUndefined(node.layout[dim[axis]]) &&
|
||||||
!isDimDefined(child, axis) &&
|
!isDimDefined(child, axis) &&
|
||||||
isPosDefined(child, leading[axis]) &&
|
isPosDefined(child, leading[axis]) &&
|
||||||
@@ -567,7 +567,7 @@ var computeLayout = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var/*int*/ ii = 0; ii < 2; ii++) {
|
for (var/*int*/ ii = 0; ii < 2; ii++) {
|
||||||
var/*css_flex_direction_t*/ axis = ii ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
var/*css_flex_direction_t*/ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
if (isPosDefined(child, trailing[axis]) &&
|
if (isPosDefined(child, trailing[axis]) &&
|
||||||
!isPosDefined(child, leading[axis])) {
|
!isPosDefined(child, leading[axis])) {
|
||||||
child.layout[leading[axis]] =
|
child.layout[leading[axis]] =
|
||||||
|
1
src/java/.idea/.name
generated
Normal file
1
src/java/.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
css-layout
|
23
src/java/.idea/compiler.xml
generated
Normal file
23
src/java/.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<option name="DEFAULT_COMPILER" value="Javac" />
|
||||||
|
<resourceExtensions />
|
||||||
|
<wildcardResourcePatterns>
|
||||||
|
<entry name="!?*.java" />
|
||||||
|
<entry name="!?*.form" />
|
||||||
|
<entry name="!?*.class" />
|
||||||
|
<entry name="!?*.groovy" />
|
||||||
|
<entry name="!?*.scala" />
|
||||||
|
<entry name="!?*.flex" />
|
||||||
|
<entry name="!?*.kt" />
|
||||||
|
<entry name="!?*.clj" />
|
||||||
|
</wildcardResourcePatterns>
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="false">
|
||||||
|
<processorPath useClasspath="true" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
5
src/java/.idea/copyright/profiles_settings.xml
generated
Normal file
5
src/java/.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="">
|
||||||
|
<module2copyright />
|
||||||
|
</settings>
|
||||||
|
</component>
|
5
src/java/.idea/encodings.xml
generated
Normal file
5
src/java/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||||
|
</project>
|
||||||
|
|
10
src/java/.idea/misc.xml
generated
Normal file
10
src/java/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<entry_points version="2.0" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
9
src/java/.idea/modules.xml
generated
Normal file
9
src/java/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/java.iml" filepath="$PROJECT_DIR$/java.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
5
src/java/.idea/scopes/scope_settings.xml
generated
Normal file
5
src/java/.idea/scopes/scope_settings.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="DependencyValidationManager">
|
||||||
|
<state>
|
||||||
|
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
||||||
|
</state>
|
||||||
|
</component>
|
125
src/java/.idea/uiDesigner.xml
generated
Normal file
125
src/java/.idea/uiDesigner.xml
generated
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
7
src/java/.idea/vcs.xml
generated
Normal file
7
src/java/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
22
src/java/java.iml
Normal file
22
src/java/java.iml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="module-library">
|
||||||
|
<library>
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$APPLICATION_HOME_DIR$/lib/junit-4.10.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</orderEntry>
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
9
src/java/src/com/facebook/csslayout/CSSAlign.java
Normal file
9
src/java/src/com/facebook/csslayout/CSSAlign.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
public enum CSSAlign {
|
||||||
|
AUTO,
|
||||||
|
FLEX_START,
|
||||||
|
CENTER,
|
||||||
|
FLEX_END,
|
||||||
|
STRETCH,
|
||||||
|
}
|
6
src/java/src/com/facebook/csslayout/CSSConstants.java
Normal file
6
src/java/src/com/facebook/csslayout/CSSConstants.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
public class CSSConstants {
|
||||||
|
|
||||||
|
public static final float UNDEFINED = Float.NaN;
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
public enum CSSFlexDirection {
|
||||||
|
COLUMN,
|
||||||
|
ROW,
|
||||||
|
}
|
9
src/java/src/com/facebook/csslayout/CSSJustify.java
Normal file
9
src/java/src/com/facebook/csslayout/CSSJustify.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
public enum CSSJustify {
|
||||||
|
FLEX_START,
|
||||||
|
CENTER,
|
||||||
|
FLEX_END,
|
||||||
|
SPACE_BETWEEN,
|
||||||
|
SPACE_AROUND,
|
||||||
|
}
|
39
src/java/src/com/facebook/csslayout/CSSLayout.java
Normal file
39
src/java/src/com/facebook/csslayout/CSSLayout.java
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
|
||||||
|
*/
|
||||||
|
public class CSSLayout {
|
||||||
|
|
||||||
|
public float x;
|
||||||
|
public float y;
|
||||||
|
public float width = CSSConstants.UNDEFINED;
|
||||||
|
public float height = CSSConstants.UNDEFINED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
|
||||||
|
*/
|
||||||
|
public void resetResult() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
width = CSSConstants.UNDEFINED;
|
||||||
|
height = CSSConstants.UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copy(CSSLayout layout) {
|
||||||
|
x = layout.x;
|
||||||
|
y = layout.y;
|
||||||
|
width = layout.width;
|
||||||
|
height = layout.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "layout: {" +
|
||||||
|
"x: " + x + ", " +
|
||||||
|
"y: " + y + ", " +
|
||||||
|
"width: " + width + ", " +
|
||||||
|
"height: " + height +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
}
|
338
src/java/src/com/facebook/csslayout/CSSNode.java
Normal file
338
src/java/src/com/facebook/csslayout/CSSNode.java
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CSS Node. It has a style object you can manipulate at {@link #style}. After calling
|
||||||
|
* {@link #calculateLayout()}, {@link #layout} will be filled with the results of the layout.
|
||||||
|
*/
|
||||||
|
public class CSSNode {
|
||||||
|
|
||||||
|
private static enum LayoutState {
|
||||||
|
/**
|
||||||
|
* Some property of this node or its children has changes and the current values in
|
||||||
|
* {@link #layout} are not valid.
|
||||||
|
*/
|
||||||
|
DIRTY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node has a new layout relative to the last time {@link #markLayoutApplied()} was called.
|
||||||
|
*/
|
||||||
|
HAS_NEW_LAYOUT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #layout} is valid for the node's properties and this layout has been marked as
|
||||||
|
* having been applied.
|
||||||
|
*/
|
||||||
|
UP_TO_DATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only one copy kept around to keep from allocating a bunch of MeasureOutput objects
|
||||||
|
// NOT THREAD SAFE! NOT RE-ENTRANT SAFE!
|
||||||
|
private static final MeasureOutput MEASURE_OUTPUT = new MeasureOutput();
|
||||||
|
|
||||||
|
public static interface MeasureFunction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should measure the given node and put the result in the given MeasureOutput.
|
||||||
|
*
|
||||||
|
* NB: measure is NOT guaranteed to be threadsafe/re-entrant safe!
|
||||||
|
*/
|
||||||
|
public void measure(CSSNode node, float width, MeasureOutput measureOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisibleForTesting
|
||||||
|
/*package*/ final CSSStyle style = new CSSStyle();
|
||||||
|
/*package*/ final CSSLayout layout = new CSSLayout();
|
||||||
|
/*package*/ final CachedCSSLayout lastLayout = new CachedCSSLayout();
|
||||||
|
|
||||||
|
// 4 is kinda arbitrary, but the default of 10 seems really high for an average View.
|
||||||
|
private final ArrayList<CSSNode> mChildren = new ArrayList<CSSNode>(4);
|
||||||
|
|
||||||
|
private CSSNode mParent;
|
||||||
|
private MeasureFunction mMeasureFunction = null;
|
||||||
|
private LayoutState mLayoutState = LayoutState.DIRTY;
|
||||||
|
|
||||||
|
public int getChildCount() {
|
||||||
|
return mChildren.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSNode getChildAt(int i) {
|
||||||
|
return mChildren.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChildAt(CSSNode child, int i) {
|
||||||
|
if (child.mParent != null) {
|
||||||
|
throw new IllegalStateException("Child already has a parent, it must be removed first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mChildren.add(i, child);
|
||||||
|
child.mParent = this;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChildAt(int i) {
|
||||||
|
mChildren.remove(i).mParent = null;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSNode getParent() {
|
||||||
|
return mParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMeasureFunction(MeasureFunction measureFunction) {
|
||||||
|
dirtyIfDifferent(mMeasureFunction, measureFunction);
|
||||||
|
mMeasureFunction = measureFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMeasureDefined() {
|
||||||
|
return mMeasureFunction != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ MeasureOutput measure(float width) {
|
||||||
|
if (!isMeasureDefined()) {
|
||||||
|
throw new RuntimeException("Measure function isn't defined!");
|
||||||
|
}
|
||||||
|
MEASURE_OUTPUT.height = CSSConstants.UNDEFINED;
|
||||||
|
MEASURE_OUTPUT.width = CSSConstants.UNDEFINED;
|
||||||
|
mMeasureFunction.measure(this, width, MEASURE_OUTPUT);
|
||||||
|
return MEASURE_OUTPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the actual layout and saves the results in {@link #layout}
|
||||||
|
*/
|
||||||
|
public void calculateLayout() {
|
||||||
|
resetLayoutResultRecursive();
|
||||||
|
LayoutEngine.layoutNode(this, CSSConstants.UNDEFINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link LayoutState#DIRTY}.
|
||||||
|
*/
|
||||||
|
/*package*/ boolean isDirty() {
|
||||||
|
return mLayoutState == LayoutState.DIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link LayoutState#HAS_NEW_LAYOUT}.
|
||||||
|
*/
|
||||||
|
public boolean hasNewLayout() {
|
||||||
|
return mLayoutState == LayoutState.HAS_NEW_LAYOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dirty() {
|
||||||
|
if (mLayoutState == LayoutState.DIRTY) {
|
||||||
|
return;
|
||||||
|
} else if (mLayoutState == LayoutState.HAS_NEW_LAYOUT) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Previous layout was ignored! markLayoutApplied() never called");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLayoutState = LayoutState.DIRTY;
|
||||||
|
|
||||||
|
if (mParent != null) {
|
||||||
|
mParent.dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ void markHasNewLayout() {
|
||||||
|
mLayoutState = LayoutState.HAS_NEW_LAYOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the node that the current values in {@link #layout} have been applied. Subsequent calls
|
||||||
|
* to {@link #hasNewLayout()} will return false until this node is laid out with new parameters.
|
||||||
|
* You must call this each time the layout is generated if the node has a new layout.
|
||||||
|
*/
|
||||||
|
public void markLayoutApplied() {
|
||||||
|
if (!hasNewLayout()) {
|
||||||
|
throw new IllegalStateException("Expected node to have a new layout to apply!");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLayoutState = LayoutState.UP_TO_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String layoutString = layout.toString();
|
||||||
|
|
||||||
|
if (getChildCount() == 0) {
|
||||||
|
return layoutString;
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutString += ", children: [\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
String childString = getChildAt(i).toString();
|
||||||
|
childString = childString.replaceAll("\n", "\n\t");
|
||||||
|
layoutString += "\t" + childString + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutString += "]";
|
||||||
|
|
||||||
|
return layoutString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetLayoutResultRecursive() {
|
||||||
|
layout.resetResult();
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
getChildAt(i).resetLayoutResultRecursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dirtyIfDifferent(float f1, float f2) {
|
||||||
|
if (!FloatUtil.floatsEqual(f1, f2)) {
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean objectsEqual(T o1, T o2) {
|
||||||
|
if (o1 == null) {
|
||||||
|
return o2 == null;
|
||||||
|
}
|
||||||
|
return o1.equals(o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> void dirtyIfDifferent(T o1, T o2) {
|
||||||
|
if (!objectsEqual(o1, o2)) {
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlexDirection(CSSFlexDirection flexDirection) {
|
||||||
|
dirtyIfDifferent(style.flexDirection, flexDirection);
|
||||||
|
style.flexDirection = flexDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJustifyContent(CSSJustify justifyContent) {
|
||||||
|
dirtyIfDifferent(style.justifyContent, justifyContent);
|
||||||
|
style.justifyContent = justifyContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlignItems(CSSAlign alignItems) {
|
||||||
|
dirtyIfDifferent(style.alignItems, alignItems);
|
||||||
|
style.alignItems = alignItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlignSelf(CSSAlign alignSelf) {
|
||||||
|
dirtyIfDifferent(style.alignSelf, alignSelf);
|
||||||
|
style.alignSelf = alignSelf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionType(CSSPositionType positionType) {
|
||||||
|
dirtyIfDifferent(style.positionType, positionType);
|
||||||
|
style.positionType = positionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlex(float flex) {
|
||||||
|
dirtyIfDifferent(style.flex, flex);
|
||||||
|
style.flex = flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarginTop(float marginTop) {
|
||||||
|
dirtyIfDifferent(style.marginTop, marginTop);
|
||||||
|
style.marginTop = marginTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarginBottom(float marginBottom) {
|
||||||
|
dirtyIfDifferent(style.marginBottom, marginBottom);
|
||||||
|
style.marginBottom = marginBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarginLeft(float marginLeft) {
|
||||||
|
dirtyIfDifferent(style.marginLeft, marginLeft);
|
||||||
|
style.marginLeft = marginLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarginRight(float marginRight) {
|
||||||
|
dirtyIfDifferent(style.marginRight, marginRight);
|
||||||
|
style.marginRight = marginRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaddingTop(float paddingTop) {
|
||||||
|
dirtyIfDifferent(style.paddingTop, paddingTop);
|
||||||
|
style.paddingTop = paddingTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaddingBottom(float paddingBottom) {
|
||||||
|
dirtyIfDifferent(style.paddingBottom, paddingBottom);
|
||||||
|
style.paddingBottom = paddingBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaddingLeft(float paddingLeft) {
|
||||||
|
dirtyIfDifferent(style.paddingLeft, paddingLeft);
|
||||||
|
style.paddingLeft = paddingLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaddingRight(float paddingRight) {
|
||||||
|
dirtyIfDifferent(style.paddingRight, paddingRight);
|
||||||
|
style.paddingRight = paddingRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionTop(float positionTop) {
|
||||||
|
dirtyIfDifferent(style.positionTop, positionTop);
|
||||||
|
style.positionTop = positionTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionBottom(float positionBottom) {
|
||||||
|
dirtyIfDifferent(style.positionBottom, positionBottom);
|
||||||
|
style.positionBottom = positionBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionLeft(float positionLeft) {
|
||||||
|
dirtyIfDifferent(style.positionLeft, positionLeft);
|
||||||
|
style.positionLeft = positionLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionRight(float positionRight) {
|
||||||
|
dirtyIfDifferent(style.positionRight, positionRight);
|
||||||
|
style.positionRight = positionRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBorderTop(float borderTop) {
|
||||||
|
dirtyIfDifferent(style.borderTop, borderTop);
|
||||||
|
style.borderTop = borderTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBorderBottom(float borderBottom) {
|
||||||
|
dirtyIfDifferent(style.borderBottom, borderBottom);
|
||||||
|
style.borderBottom = borderBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBorderLeft(float borderLeft) {
|
||||||
|
dirtyIfDifferent(style.borderLeft, borderLeft);
|
||||||
|
style.borderLeft = borderLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBorderRight(float borderRight) {
|
||||||
|
dirtyIfDifferent(style.borderRight, borderRight);
|
||||||
|
style.borderRight = borderRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStyleWidth(float width) {
|
||||||
|
dirtyIfDifferent(style.width, width);
|
||||||
|
style.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStyleHeight(float height) {
|
||||||
|
dirtyIfDifferent(style.height, height);
|
||||||
|
style.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLayoutX() {
|
||||||
|
return layout.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLayoutY() {
|
||||||
|
return layout.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLayoutWidth() {
|
||||||
|
return layout.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLayoutHeight() {
|
||||||
|
return layout.height;
|
||||||
|
}
|
||||||
|
}
|
6
src/java/src/com/facebook/csslayout/CSSPositionType.java
Normal file
6
src/java/src/com/facebook/csslayout/CSSPositionType.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
public enum CSSPositionType {
|
||||||
|
RELATIVE,
|
||||||
|
ABSOLUTE,
|
||||||
|
}
|
37
src/java/src/com/facebook/csslayout/CSSStyle.java
Normal file
37
src/java/src/com/facebook/csslayout/CSSStyle.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CSS style definition for a {@link CSSNode}.
|
||||||
|
*/
|
||||||
|
public class CSSStyle {
|
||||||
|
|
||||||
|
public CSSFlexDirection flexDirection = CSSFlexDirection.COLUMN;
|
||||||
|
public CSSJustify justifyContent = CSSJustify.FLEX_START;
|
||||||
|
public CSSAlign alignItems = CSSAlign.FLEX_START;
|
||||||
|
public CSSAlign alignSelf = CSSAlign.AUTO;
|
||||||
|
public CSSPositionType positionType = CSSPositionType.RELATIVE;
|
||||||
|
public float flex;
|
||||||
|
|
||||||
|
public float marginTop;
|
||||||
|
public float marginBottom;
|
||||||
|
public float marginLeft;
|
||||||
|
public float marginRight;
|
||||||
|
|
||||||
|
public float paddingTop;
|
||||||
|
public float paddingBottom;
|
||||||
|
public float paddingLeft;
|
||||||
|
public float paddingRight;
|
||||||
|
|
||||||
|
public float positionTop = CSSConstants.UNDEFINED;
|
||||||
|
public float positionBottom = CSSConstants.UNDEFINED;
|
||||||
|
public float positionLeft = CSSConstants.UNDEFINED;
|
||||||
|
public float positionRight = CSSConstants.UNDEFINED;
|
||||||
|
|
||||||
|
public float borderTop;
|
||||||
|
public float borderBottom;
|
||||||
|
public float borderLeft;
|
||||||
|
public float borderRight;
|
||||||
|
|
||||||
|
public float width = CSSConstants.UNDEFINED;
|
||||||
|
public float height = CSSConstants.UNDEFINED;
|
||||||
|
}
|
13
src/java/src/com/facebook/csslayout/CachedCSSLayout.java
Normal file
13
src/java/src/com/facebook/csslayout/CachedCSSLayout.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSSLayout with additional information about the conditions under which it was generated.
|
||||||
|
* {@link #requestedWidth} and {@link #requestedHeight} are the width and height the parent set on
|
||||||
|
* this node before calling layout visited us.
|
||||||
|
*/
|
||||||
|
public class CachedCSSLayout extends CSSLayout {
|
||||||
|
|
||||||
|
public float requestedWidth = CSSConstants.UNDEFINED;
|
||||||
|
public float requestedHeight = CSSConstants.UNDEFINED;
|
||||||
|
public float parentMaxWidth = CSSConstants.UNDEFINED;
|
||||||
|
}
|
17
src/java/src/com/facebook/csslayout/FloatUtil.java
Normal file
17
src/java/src/com/facebook/csslayout/FloatUtil.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
public class FloatUtil {
|
||||||
|
|
||||||
|
public static boolean isUndefined(float f) {
|
||||||
|
return Float.compare(f, CSSConstants.UNDEFINED) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean floatsEqual(float f1, float f2) {
|
||||||
|
if (isUndefined(f1)) {
|
||||||
|
return isUndefined(f2);
|
||||||
|
}
|
||||||
|
return Math.abs(f2 - f1) < .00001;
|
||||||
|
}
|
||||||
|
}
|
650
src/java/src/com/facebook/csslayout/LayoutEngine.java
Normal file
650
src/java/src/com/facebook/csslayout/LayoutEngine.java
Normal file
@@ -0,0 +1,650 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates layouts based on CSS style. See {@link #layoutNode(CSSNode, float)}.
|
||||||
|
*/
|
||||||
|
public class LayoutEngine {
|
||||||
|
|
||||||
|
private static enum PositionIndex {
|
||||||
|
TOP,
|
||||||
|
LEFT,
|
||||||
|
BOTTOM,
|
||||||
|
RIGHT,
|
||||||
|
}
|
||||||
|
|
||||||
|
private static enum DimensionIndex {
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT,
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setLayoutPosition(CSSNode node, PositionIndex position, float value) {
|
||||||
|
switch (position) {
|
||||||
|
case TOP:
|
||||||
|
node.layout.y = value;
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
node.layout.x = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Didn't get TOP or LEFT!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getLayoutPosition(CSSNode node, PositionIndex position) {
|
||||||
|
switch (position) {
|
||||||
|
case TOP:
|
||||||
|
return node.layout.y;
|
||||||
|
case LEFT:
|
||||||
|
return node.layout.x;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Didn't get TOP or LEFT!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setLayoutDimension(CSSNode node, DimensionIndex dimension, float value) {
|
||||||
|
switch (dimension) {
|
||||||
|
case WIDTH:
|
||||||
|
node.layout.width = value;
|
||||||
|
break;
|
||||||
|
case HEIGHT:
|
||||||
|
node.layout.height = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a third dimension...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getLayoutDimension(CSSNode node, DimensionIndex dimension) {
|
||||||
|
switch (dimension) {
|
||||||
|
case WIDTH:
|
||||||
|
return node.layout.width;
|
||||||
|
case HEIGHT:
|
||||||
|
return node.layout.height;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a third dimension...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getStylePosition(CSSNode node, PositionIndex position) {
|
||||||
|
switch (position) {
|
||||||
|
case TOP:
|
||||||
|
return node.style.positionTop;
|
||||||
|
case BOTTOM:
|
||||||
|
return node.style.positionBottom;
|
||||||
|
case LEFT:
|
||||||
|
return node.style.positionLeft;
|
||||||
|
case RIGHT:
|
||||||
|
return node.style.positionRight;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a new cardinal direction...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getStyleDimension(CSSNode node, DimensionIndex dimension) {
|
||||||
|
switch (dimension) {
|
||||||
|
case WIDTH:
|
||||||
|
return node.style.width;
|
||||||
|
case HEIGHT:
|
||||||
|
return node.style.height;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a third dimension...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PositionIndex getLeading(CSSFlexDirection axis) {
|
||||||
|
return axis == CSSFlexDirection.COLUMN ? PositionIndex.TOP : PositionIndex.LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PositionIndex getTrailing(CSSFlexDirection axis) {
|
||||||
|
return axis == CSSFlexDirection.COLUMN ? PositionIndex.BOTTOM : PositionIndex.RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PositionIndex getPos(CSSFlexDirection axis) {
|
||||||
|
return axis == CSSFlexDirection.COLUMN ? PositionIndex.TOP : PositionIndex.LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DimensionIndex getDim(CSSFlexDirection axis) {
|
||||||
|
return axis == CSSFlexDirection.COLUMN ? DimensionIndex.HEIGHT : DimensionIndex.WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDimDefined(CSSNode node, CSSFlexDirection axis) {
|
||||||
|
return !FloatUtil.isUndefined(getStyleDimension(node, getDim(axis)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPosDefined(CSSNode node, PositionIndex position) {
|
||||||
|
return !FloatUtil.isUndefined(getStylePosition(node, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getPosition(CSSNode node, PositionIndex position) {
|
||||||
|
float result = getStylePosition(node, position);
|
||||||
|
return FloatUtil.isUndefined(result) ? 0 : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getMargin(CSSNode node, PositionIndex position) {
|
||||||
|
switch (position) {
|
||||||
|
case TOP:
|
||||||
|
return node.style.marginTop;
|
||||||
|
case BOTTOM:
|
||||||
|
return node.style.marginBottom;
|
||||||
|
case LEFT:
|
||||||
|
return node.style.marginLeft;
|
||||||
|
case RIGHT:
|
||||||
|
return node.style.marginRight;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a new cardinal direction...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getPadding(CSSNode node, PositionIndex position) {
|
||||||
|
switch (position) {
|
||||||
|
case TOP:
|
||||||
|
return node.style.paddingTop;
|
||||||
|
case BOTTOM:
|
||||||
|
return node.style.paddingBottom;
|
||||||
|
case LEFT:
|
||||||
|
return node.style.paddingLeft;
|
||||||
|
case RIGHT:
|
||||||
|
return node.style.paddingRight;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a new cardinal direction...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getBorder(CSSNode node, PositionIndex position) {
|
||||||
|
switch (position) {
|
||||||
|
case TOP:
|
||||||
|
return node.style.borderTop;
|
||||||
|
case BOTTOM:
|
||||||
|
return node.style.borderBottom;
|
||||||
|
case LEFT:
|
||||||
|
return node.style.borderLeft;
|
||||||
|
case RIGHT:
|
||||||
|
return node.style.borderRight;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Someone added a new cardinal direction...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getPaddingAndBorder(CSSNode node, PositionIndex position) {
|
||||||
|
return getPadding(node, position) + getBorder(node, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getMarginAxis(CSSNode node, CSSFlexDirection axis) {
|
||||||
|
return getMargin(node, getLeading(axis)) + getMargin(node, getTrailing(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getPaddingAndBorderAxis(CSSNode node, CSSFlexDirection axis) {
|
||||||
|
return getPaddingAndBorder(
|
||||||
|
node,
|
||||||
|
getLeading(axis)) + getPaddingAndBorder(node, getTrailing(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setDimensionFromStyle(CSSNode node, CSSFlexDirection axis) {
|
||||||
|
// The parent already computed us a width or height. We just skip it
|
||||||
|
if (!FloatUtil.isUndefined(getLayoutDimension(node, getDim(axis)))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We only run if there's a width or height defined
|
||||||
|
if (!isDimDefined(node, axis)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dimensions can never be smaller than the padding and border
|
||||||
|
float maxLayoutDimension = Math.max(
|
||||||
|
getStyleDimension(node, getDim(axis)),
|
||||||
|
getPaddingAndBorderAxis(node, axis));
|
||||||
|
setLayoutDimension(node, getDim(axis), maxLayoutDimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getRelativePosition(CSSNode node, CSSFlexDirection axis) {
|
||||||
|
float lead = getStylePosition(node, getLeading(axis));
|
||||||
|
if (!FloatUtil.isUndefined(lead)) {
|
||||||
|
return lead;
|
||||||
|
}
|
||||||
|
return -getPosition(node, getTrailing(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getFlex(CSSNode node) {
|
||||||
|
return node.style.flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CSSFlexDirection getFlexDirection(CSSNode node) {
|
||||||
|
return node.style.flexDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CSSPositionType getPositionType(CSSNode node) {
|
||||||
|
return node.style.positionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CSSAlign getAlignItem(CSSNode node, CSSNode child) {
|
||||||
|
if (child.style.alignSelf != CSSAlign.AUTO) {
|
||||||
|
return child.style.alignSelf;
|
||||||
|
}
|
||||||
|
return node.style.alignItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CSSJustify getJustifyContent(CSSNode node) {
|
||||||
|
return node.style.justifyContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isFlex(CSSNode node) {
|
||||||
|
return getPositionType(node) == CSSPositionType.RELATIVE && getFlex(node) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMeasureDefined(CSSNode node) {
|
||||||
|
return node.isMeasureDefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getDimWithMargin(CSSNode node, CSSFlexDirection axis) {
|
||||||
|
return getLayoutDimension(node, getDim(axis)) +
|
||||||
|
getMargin(node, getLeading(axis)) +
|
||||||
|
getMargin(node, getTrailing(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean needsRelayout(CSSNode node, float parentMaxWidth) {
|
||||||
|
return node.isDirty() ||
|
||||||
|
!FloatUtil.floatsEqual(node.lastLayout.requestedHeight, node.layout.height) ||
|
||||||
|
!FloatUtil.floatsEqual(node.lastLayout.requestedWidth, node.layout.width) ||
|
||||||
|
!FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ static void layoutNode(CSSNode node, float parentMaxWidth) {
|
||||||
|
if (needsRelayout(node, parentMaxWidth)) {
|
||||||
|
node.lastLayout.requestedWidth = node.layout.width;
|
||||||
|
node.lastLayout.requestedHeight = node.layout.height;
|
||||||
|
node.lastLayout.parentMaxWidth = parentMaxWidth;
|
||||||
|
|
||||||
|
layoutNodeImpl(node, parentMaxWidth);
|
||||||
|
node.markHasNewLayout();
|
||||||
|
|
||||||
|
node.lastLayout.copy(node.layout);
|
||||||
|
} else {
|
||||||
|
node.layout.copy(node.lastLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void layoutNodeImpl(CSSNode node, float parentMaxWidth) {
|
||||||
|
|
||||||
|
/** START_GENERATED **/
|
||||||
|
|
||||||
|
CSSFlexDirection mainAxis = getFlexDirection(node);
|
||||||
|
CSSFlexDirection crossAxis = mainAxis == CSSFlexDirection.ROW ?
|
||||||
|
CSSFlexDirection.COLUMN :
|
||||||
|
CSSFlexDirection.ROW;
|
||||||
|
|
||||||
|
// Handle width and height style attributes
|
||||||
|
setDimensionFromStyle(node, mainAxis);
|
||||||
|
setDimensionFromStyle(node, crossAxis);
|
||||||
|
|
||||||
|
// The position is set by the parent, but we need to complete it with a
|
||||||
|
// delta composed of the margin and left/top/right/bottom
|
||||||
|
setLayoutPosition(node, getLeading(mainAxis), getLayoutPosition(node, getLeading(mainAxis)) + getMargin(node, getLeading(mainAxis)) +
|
||||||
|
getRelativePosition(node, mainAxis));
|
||||||
|
setLayoutPosition(node, getLeading(crossAxis), getLayoutPosition(node, getLeading(crossAxis)) + getMargin(node, getLeading(crossAxis)) +
|
||||||
|
getRelativePosition(node, crossAxis));
|
||||||
|
|
||||||
|
if (isMeasureDefined(node)) {
|
||||||
|
float width = CSSConstants.UNDEFINED;
|
||||||
|
if (isDimDefined(node, CSSFlexDirection.ROW)) {
|
||||||
|
width = node.style.width;
|
||||||
|
} else if (!FloatUtil.isUndefined(getLayoutDimension(node, getDim(CSSFlexDirection.ROW)))) {
|
||||||
|
width = getLayoutDimension(node, getDim(CSSFlexDirection.ROW));
|
||||||
|
} else {
|
||||||
|
width = parentMaxWidth -
|
||||||
|
getMarginAxis(node, CSSFlexDirection.ROW);
|
||||||
|
}
|
||||||
|
width -= getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
|
||||||
|
|
||||||
|
// We only need to give a dimension for the text if we haven't got any
|
||||||
|
// for it computed yet. It can either be from the style attribute or because
|
||||||
|
// the element is flexible.
|
||||||
|
boolean isRowUndefined = !isDimDefined(node, CSSFlexDirection.ROW) &&
|
||||||
|
FloatUtil.isUndefined(getLayoutDimension(node, getDim(CSSFlexDirection.ROW)));
|
||||||
|
boolean isColumnUndefined = !isDimDefined(node, CSSFlexDirection.COLUMN) &&
|
||||||
|
FloatUtil.isUndefined(getLayoutDimension(node, getDim(CSSFlexDirection.COLUMN)));
|
||||||
|
|
||||||
|
// Let's not measure the text if we already know both dimensions
|
||||||
|
if (isRowUndefined || isColumnUndefined) {
|
||||||
|
MeasureOutput measure_dim = node.measure(
|
||||||
|
width
|
||||||
|
);
|
||||||
|
if (isRowUndefined) {
|
||||||
|
node.layout.width = measure_dim.width +
|
||||||
|
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
|
||||||
|
}
|
||||||
|
if (isColumnUndefined) {
|
||||||
|
node.layout.height = measure_dim.height +
|
||||||
|
getPaddingAndBorderAxis(node, CSSFlexDirection.COLUMN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-fill some dimensions straight from the parent
|
||||||
|
for (int i = 0; i < node.getChildCount(); ++i) {
|
||||||
|
CSSNode child = node.getChildAt(i);
|
||||||
|
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||||
|
// we call the recursive layout pass
|
||||||
|
if (getAlignItem(node, child) == CSSAlign.STRETCH &&
|
||||||
|
getPositionType(child) == CSSPositionType.RELATIVE &&
|
||||||
|
!FloatUtil.isUndefined(getLayoutDimension(node, getDim(crossAxis))) &&
|
||||||
|
!isDimDefined(child, crossAxis) &&
|
||||||
|
!isPosDefined(child, getLeading(crossAxis))) {
|
||||||
|
setLayoutDimension(child, getDim(crossAxis), Math.max(
|
||||||
|
getLayoutDimension(node, getDim(crossAxis)) -
|
||||||
|
getPaddingAndBorderAxis(node, crossAxis) -
|
||||||
|
getMarginAxis(child, crossAxis),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
getPaddingAndBorderAxis(child, crossAxis)
|
||||||
|
));
|
||||||
|
} else if (getPositionType(child) == CSSPositionType.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++) {
|
||||||
|
CSSFlexDirection axis = (ii != 0) ? CSSFlexDirection.ROW : CSSFlexDirection.COLUMN;
|
||||||
|
if (!FloatUtil.isUndefined(getLayoutDimension(node, getDim(axis))) &&
|
||||||
|
!isDimDefined(child, axis) &&
|
||||||
|
isPosDefined(child, getLeading(axis)) &&
|
||||||
|
isPosDefined(child, getTrailing(axis))) {
|
||||||
|
setLayoutDimension(child, getDim(axis), Math.max(
|
||||||
|
getLayoutDimension(node, getDim(axis)) -
|
||||||
|
getPaddingAndBorderAxis(node, axis) -
|
||||||
|
getMarginAxis(child, axis) -
|
||||||
|
getPosition(child, getLeading(axis)) -
|
||||||
|
getPosition(child, getTrailing(axis)),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
getPaddingAndBorderAxis(child, axis)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop A> Layout non flexible children and count children by type
|
||||||
|
|
||||||
|
// mainContentDim is accumulation of the dimensions and margin of all the
|
||||||
|
// non flexible children. This will be used in order to either set the
|
||||||
|
// dimensions of the node if none already exist, or to compute the
|
||||||
|
// remaining space left for the flexible children.
|
||||||
|
float mainContentDim = 0;
|
||||||
|
|
||||||
|
// There are three kind of children, non flexible, flexible and absolute.
|
||||||
|
// We need to know how many there are in order to distribute the space.
|
||||||
|
int flexibleChildrenCount = 0;
|
||||||
|
float totalFlexible = 0;
|
||||||
|
int nonFlexibleChildrenCount = 0;
|
||||||
|
for (int i = 0; i < node.getChildCount(); ++i) {
|
||||||
|
CSSNode child = node.getChildAt(i);
|
||||||
|
|
||||||
|
// It only makes sense to consider a child flexible if we have a computed
|
||||||
|
// dimension for the node.
|
||||||
|
if (!FloatUtil.isUndefined(getLayoutDimension(node, getDim(mainAxis))) && isFlex(child)) {
|
||||||
|
flexibleChildrenCount++;
|
||||||
|
totalFlexible = 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
|
||||||
|
// remaining space.
|
||||||
|
mainContentDim = mainContentDim + getPaddingAndBorderAxis(child, mainAxis) +
|
||||||
|
getMarginAxis(child, mainAxis);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
float maxWidth = CSSConstants.UNDEFINED;
|
||||||
|
if (mainAxis == CSSFlexDirection.ROW) {
|
||||||
|
// do nothing
|
||||||
|
} else if (isDimDefined(node, CSSFlexDirection.ROW)) {
|
||||||
|
maxWidth = getLayoutDimension(node, getDim(CSSFlexDirection.ROW)) -
|
||||||
|
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
|
||||||
|
} else {
|
||||||
|
maxWidth = parentMaxWidth -
|
||||||
|
getMarginAxis(node, CSSFlexDirection.ROW) -
|
||||||
|
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the main recursive call. We layout non flexible children.
|
||||||
|
layoutNode(child, maxWidth);
|
||||||
|
|
||||||
|
// Absolute positioned elements do not take part of the layout, so we
|
||||||
|
// don't use them to compute mainContentDim
|
||||||
|
if (getPositionType(child) == CSSPositionType.RELATIVE) {
|
||||||
|
nonFlexibleChildrenCount++;
|
||||||
|
// At this point we know the final size and margin of the element.
|
||||||
|
mainContentDim = mainContentDim + getDimWithMargin(child, mainAxis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// <Loop B> Layout flexible children and allocate empty space
|
||||||
|
|
||||||
|
// In order to position the elements in the main axis, we have two
|
||||||
|
// controls. The space between the beginning and the first element
|
||||||
|
// and the space between each two elements.
|
||||||
|
float leadingMainDim = 0;
|
||||||
|
float betweenMainDim = 0;
|
||||||
|
|
||||||
|
// If the dimensions of the current node is defined by its children, they
|
||||||
|
// are all going to be packed together and we don't need to compute
|
||||||
|
// anything.
|
||||||
|
if (!FloatUtil.isUndefined(getLayoutDimension(node, getDim(mainAxis)))) {
|
||||||
|
// The remaining available space that needs to be allocated
|
||||||
|
float remainingMainDim = getLayoutDimension(node, getDim(mainAxis)) -
|
||||||
|
getPaddingAndBorderAxis(node, mainAxis) -
|
||||||
|
mainContentDim;
|
||||||
|
|
||||||
|
// If there are flexible children in the mix, they are going to fill the
|
||||||
|
// remaining space
|
||||||
|
if (flexibleChildrenCount != 0) {
|
||||||
|
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.
|
||||||
|
if (flexibleMainDim < 0) {
|
||||||
|
flexibleMainDim = 0;
|
||||||
|
}
|
||||||
|
// We iterate over the full array and only apply the action on flexible
|
||||||
|
// children. This is faster than actually allocating a new array that
|
||||||
|
// contains only flexible children.
|
||||||
|
for (int i = 0; i < node.getChildCount(); ++i) {
|
||||||
|
CSSNode child = node.getChildAt(i);
|
||||||
|
if (isFlex(child)) {
|
||||||
|
// At this point we know the final size of the element in the main
|
||||||
|
// dimension
|
||||||
|
setLayoutDimension(child, getDim(mainAxis), flexibleMainDim * getFlex(child) +
|
||||||
|
getPaddingAndBorderAxis(child, mainAxis));
|
||||||
|
|
||||||
|
float maxWidth = CSSConstants.UNDEFINED;
|
||||||
|
if (mainAxis == CSSFlexDirection.ROW) {
|
||||||
|
// do nothing
|
||||||
|
} else if (isDimDefined(node, CSSFlexDirection.ROW)) {
|
||||||
|
maxWidth = getLayoutDimension(node, getDim(CSSFlexDirection.ROW)) -
|
||||||
|
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
|
||||||
|
} else {
|
||||||
|
maxWidth = parentMaxWidth -
|
||||||
|
getMarginAxis(node, CSSFlexDirection.ROW) -
|
||||||
|
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And we recursively call the layout algorithm for this child
|
||||||
|
layoutNode(child, maxWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use justifyContent to figure out how to allocate the remaining
|
||||||
|
// space available
|
||||||
|
} else {
|
||||||
|
CSSJustify justifyContent = getJustifyContent(node);
|
||||||
|
if (justifyContent == CSSJustify.FLEX_START) {
|
||||||
|
// Do nothing
|
||||||
|
} else if (justifyContent == CSSJustify.CENTER) {
|
||||||
|
leadingMainDim = remainingMainDim / 2;
|
||||||
|
} else if (justifyContent == CSSJustify.FLEX_END) {
|
||||||
|
leadingMainDim = remainingMainDim;
|
||||||
|
} else if (justifyContent == CSSJustify.SPACE_BETWEEN) {
|
||||||
|
remainingMainDim = Math.max(remainingMainDim, 0);
|
||||||
|
betweenMainDim = remainingMainDim /
|
||||||
|
(flexibleChildrenCount + nonFlexibleChildrenCount - 1);
|
||||||
|
} else if (justifyContent == CSSJustify.SPACE_AROUND) {
|
||||||
|
// Space on the edges is half of the space between elements
|
||||||
|
betweenMainDim = remainingMainDim /
|
||||||
|
(flexibleChildrenCount + nonFlexibleChildrenCount);
|
||||||
|
leadingMainDim = betweenMainDim / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop C> Position elements in the main axis and compute dimensions
|
||||||
|
|
||||||
|
// At this point, all the children have their dimensions set. We need to
|
||||||
|
// find their position. In order to do that, we accumulate data in
|
||||||
|
// variables that are also useful to compute the total dimensions of the
|
||||||
|
// container!
|
||||||
|
float crossDim = 0;
|
||||||
|
float mainDim = leadingMainDim +
|
||||||
|
getPaddingAndBorder(node, getLeading(mainAxis));
|
||||||
|
for (int i = 0; i < node.getChildCount(); ++i) {
|
||||||
|
CSSNode child = node.getChildAt(i);
|
||||||
|
|
||||||
|
if (getPositionType(child) == CSSPositionType.ABSOLUTE &&
|
||||||
|
isPosDefined(child, getLeading(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).
|
||||||
|
setLayoutPosition(child, getPos(mainAxis), getPosition(child, getLeading(mainAxis)) +
|
||||||
|
getBorder(node, getLeading(mainAxis)) +
|
||||||
|
getMargin(child, getLeading(mainAxis)));
|
||||||
|
} else {
|
||||||
|
// If the child is position absolute (without top/left) or relative,
|
||||||
|
// we put it at the current accumulated offset.
|
||||||
|
setLayoutPosition(child, getPos(mainAxis), getLayoutPosition(child, getPos(mainAxis)) + mainDim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we placed the element, we need to update the variables
|
||||||
|
// We only need to do that for relative elements. Absolute elements
|
||||||
|
// do not take part in that phase.
|
||||||
|
if (getPositionType(child) == CSSPositionType.RELATIVE) {
|
||||||
|
// The main dimension is the sum of all the elements dimension plus
|
||||||
|
// the spacing.
|
||||||
|
mainDim = mainDim + betweenMainDim + getDimWithMargin(child, mainAxis);
|
||||||
|
// The cross dimension is the max of the elements dimension since there
|
||||||
|
// can only be one element in that cross dimension.
|
||||||
|
crossDim = Math.max(crossDim, getDimWithMargin(child, crossAxis));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user didn't specify a width or height, and it has not been set
|
||||||
|
// by the container, then we set it via the children.
|
||||||
|
if (FloatUtil.isUndefined(getLayoutDimension(node, getDim(mainAxis)))) {
|
||||||
|
setLayoutDimension(node, getDim(mainAxis), Math.max(
|
||||||
|
// We're missing the last padding at this point to get the final
|
||||||
|
// dimension
|
||||||
|
mainDim + getPaddingAndBorder(node, getTrailing(mainAxis)),
|
||||||
|
// We can never assign a width smaller than the padding and borders
|
||||||
|
getPaddingAndBorderAxis(node, mainAxis)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FloatUtil.isUndefined(getLayoutDimension(node, getDim(crossAxis)))) {
|
||||||
|
setLayoutDimension(node, getDim(crossAxis), Math.max(
|
||||||
|
// For the cross dim, we add both sides at the end because the value
|
||||||
|
// is aggregate via a max function. Intermediate negative values
|
||||||
|
// can mess this computation otherwise
|
||||||
|
crossDim + getPaddingAndBorderAxis(node, crossAxis),
|
||||||
|
getPaddingAndBorderAxis(node, crossAxis)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// <Loop D> Position elements in the cross axis
|
||||||
|
|
||||||
|
for (int i = 0; i < node.getChildCount(); ++i) {
|
||||||
|
CSSNode child = node.getChildAt(i);
|
||||||
|
|
||||||
|
if (getPositionType(child) == CSSPositionType.ABSOLUTE &&
|
||||||
|
isPosDefined(child, getLeading(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.
|
||||||
|
setLayoutPosition(child, getPos(crossAxis), getPosition(child, getLeading(crossAxis)) +
|
||||||
|
getBorder(node, getLeading(crossAxis)) +
|
||||||
|
getMargin(child, getLeading(crossAxis)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
float leadingCrossDim = getPaddingAndBorder(node, getLeading(crossAxis));
|
||||||
|
|
||||||
|
// For a relative children, we're either using alignItems (parent) or
|
||||||
|
// alignSelf (child) in order to determine the position in the cross axis
|
||||||
|
if (getPositionType(child) == CSSPositionType.RELATIVE) {
|
||||||
|
CSSAlign alignItem = getAlignItem(node, child);
|
||||||
|
if (alignItem == CSSAlign.FLEX_START) {
|
||||||
|
// Do nothing
|
||||||
|
} else if (alignItem == CSSAlign.STRETCH) {
|
||||||
|
// You can only stretch if the dimension has not already been set
|
||||||
|
// previously.
|
||||||
|
if (!isDimDefined(child, crossAxis)) {
|
||||||
|
setLayoutDimension(child, getDim(crossAxis), Math.max(
|
||||||
|
getLayoutDimension(node, getDim(crossAxis)) -
|
||||||
|
getPaddingAndBorderAxis(node, crossAxis) -
|
||||||
|
getMarginAxis(child, crossAxis),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
getPaddingAndBorderAxis(child, crossAxis)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The remaining space between the parent dimensions+padding and child
|
||||||
|
// dimensions+margin.
|
||||||
|
float remainingCrossDim = getLayoutDimension(node, getDim(crossAxis)) -
|
||||||
|
getPaddingAndBorderAxis(node, crossAxis) -
|
||||||
|
getDimWithMargin(child, crossAxis);
|
||||||
|
|
||||||
|
if (alignItem == CSSAlign.CENTER) {
|
||||||
|
leadingCrossDim = leadingCrossDim + remainingCrossDim / 2;
|
||||||
|
} else { // CSSAlign.FLEX_END
|
||||||
|
leadingCrossDim = leadingCrossDim + remainingCrossDim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And we apply the position
|
||||||
|
setLayoutPosition(child, getPos(crossAxis), getLayoutPosition(child, getPos(crossAxis)) + leadingCrossDim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop E> Calculate dimensions for absolutely positioned elements
|
||||||
|
|
||||||
|
for (int i = 0; i < node.getChildCount(); ++i) {
|
||||||
|
CSSNode child = node.getChildAt(i);
|
||||||
|
if (getPositionType(child) == CSSPositionType.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++) {
|
||||||
|
CSSFlexDirection axis = (ii != 0) ? CSSFlexDirection.ROW : CSSFlexDirection.COLUMN;
|
||||||
|
if (!FloatUtil.isUndefined(getLayoutDimension(node, getDim(axis))) &&
|
||||||
|
!isDimDefined(child, axis) &&
|
||||||
|
isPosDefined(child, getLeading(axis)) &&
|
||||||
|
isPosDefined(child, getTrailing(axis))) {
|
||||||
|
setLayoutDimension(child, getDim(axis), Math.max(
|
||||||
|
getLayoutDimension(node, getDim(axis)) -
|
||||||
|
getPaddingAndBorderAxis(node, axis) -
|
||||||
|
getMarginAxis(child, axis) -
|
||||||
|
getPosition(child, getLeading(axis)) -
|
||||||
|
getPosition(child, getTrailing(axis)),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
getPaddingAndBorderAxis(child, axis)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int ii = 0; ii < 2; ii++) {
|
||||||
|
CSSFlexDirection axis = (ii != 0) ? CSSFlexDirection.ROW : CSSFlexDirection.COLUMN;
|
||||||
|
if (isPosDefined(child, getTrailing(axis)) &&
|
||||||
|
!isPosDefined(child, getLeading(axis))) {
|
||||||
|
setLayoutPosition(child, getLeading(axis), getLayoutDimension(node, getDim(axis)) -
|
||||||
|
getLayoutDimension(child, getDim(axis)) -
|
||||||
|
getPosition(child, getTrailing(axis)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** END_GENERATED **/
|
||||||
|
}
|
10
src/java/src/com/facebook/csslayout/MeasureOutput.java
Normal file
10
src/java/src/com/facebook/csslayout/MeasureOutput.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POJO to hold the output of the measure function.
|
||||||
|
*/
|
||||||
|
public class MeasureOutput {
|
||||||
|
|
||||||
|
public float width;
|
||||||
|
public float height;
|
||||||
|
}
|
42
src/java/tests/com/facebook/csslayout/CSSNodeTest.java
Normal file
42
src/java/tests/com/facebook/csslayout/CSSNodeTest.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CSSNode}.
|
||||||
|
*/
|
||||||
|
public class CSSNodeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddChildGetParent() {
|
||||||
|
CSSNode parent = new CSSNode();
|
||||||
|
CSSNode child = new CSSNode();
|
||||||
|
|
||||||
|
assertNull(child.getParent());
|
||||||
|
assertEquals(0, parent.getChildCount());
|
||||||
|
|
||||||
|
parent.addChildAt(child, 0);
|
||||||
|
|
||||||
|
assertEquals(1, parent.getChildCount());
|
||||||
|
assertEquals(child, parent.getChildAt(0));
|
||||||
|
assertEquals(parent, child.getParent());
|
||||||
|
|
||||||
|
parent.removeChildAt(0);
|
||||||
|
|
||||||
|
assertNull(child.getParent());
|
||||||
|
assertEquals(0, parent.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void testCannotAddChildToMultipleParents() {
|
||||||
|
CSSNode parent1 = new CSSNode();
|
||||||
|
CSSNode parent2 = new CSSNode();
|
||||||
|
CSSNode child = new CSSNode();
|
||||||
|
|
||||||
|
parent1.addChildAt(child, 0);
|
||||||
|
parent2.addChildAt(child, 0);
|
||||||
|
}
|
||||||
|
}
|
180
src/java/tests/com/facebook/csslayout/LayoutCachingTest.java
Normal file
180
src/java/tests/com/facebook/csslayout/LayoutCachingTest.java
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link LayoutEngine} and {@link CSSNode} to make sure layouts are only generated when
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
|
public class LayoutCachingTest {
|
||||||
|
|
||||||
|
private void assertTreeHasNewLayout(boolean expectedHasNewLayout, CSSNode root) {
|
||||||
|
assertEquals(expectedHasNewLayout, root.hasNewLayout());
|
||||||
|
|
||||||
|
for (int i = 0; i < root.getChildCount(); i++) {
|
||||||
|
assertTreeHasNewLayout(expectedHasNewLayout, root.getChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markLayoutAppliedForTree(CSSNode root) {
|
||||||
|
root.markLayoutApplied();
|
||||||
|
for (int i = 0; i < root.getChildCount(); i++) {
|
||||||
|
markLayoutAppliedForTree(root.getChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCachesFullTree() {
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(true, root);
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidatesCacheWhenChildAdded() {
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
CSSNode c0c1 = new CSSNode();
|
||||||
|
c0c1.setStyleWidth(200);
|
||||||
|
c0c1.setStyleHeight(200);
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(true, root);
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
|
||||||
|
c0.addChildAt(c0c1, 1);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTrue(root.hasNewLayout());
|
||||||
|
assertTrue(c0.hasNewLayout());
|
||||||
|
assertTrue(c0c1.hasNewLayout());
|
||||||
|
|
||||||
|
assertFalse(c0c0.hasNewLayout());
|
||||||
|
assertFalse(c1.hasNewLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidatesCacheWhenEnumPropertyChanges() {
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(true, root);
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
|
||||||
|
c1.setAlignSelf(CSSAlign.CENTER);
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
assertTrue(root.hasNewLayout());
|
||||||
|
assertTrue(c1.hasNewLayout());
|
||||||
|
|
||||||
|
assertFalse(c0.hasNewLayout());
|
||||||
|
assertFalse(c0c0.hasNewLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidatesCacheWhenFloatPropertyChanges() {
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(true, root);
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
|
||||||
|
c1.setMarginLeft(10);
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
assertTrue(root.hasNewLayout());
|
||||||
|
assertTrue(c1.hasNewLayout());
|
||||||
|
|
||||||
|
assertFalse(c0.hasNewLayout());
|
||||||
|
assertFalse(c0c0.hasNewLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidatesFullTreeWhenParentWidthChanges() {
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(true, root);
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
|
||||||
|
c0.setStyleWidth(200);
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
assertTrue(root.hasNewLayout());
|
||||||
|
assertTrue(c0.hasNewLayout());
|
||||||
|
assertTrue(c0c0.hasNewLayout());
|
||||||
|
|
||||||
|
assertFalse(c1.hasNewLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesNotInvalidateCacheWhenPropertyIsTheSame() {
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
root.setStyleWidth(200);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(true, root);
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
|
||||||
|
root.setStyleWidth(200);
|
||||||
|
root.calculateLayout();
|
||||||
|
assertTreeHasNewLayout(false, root);
|
||||||
|
}
|
||||||
|
}
|
3722
src/java/tests/com/facebook/csslayout/LayoutEngineTest.java
Normal file
3722
src/java/tests/com/facebook/csslayout/LayoutEngineTest.java
Normal file
File diff suppressed because it is too large
Load Diff
17
src/java/tests/com/facebook/csslayout/TestConstants.java
Normal file
17
src/java/tests/com/facebook/csslayout/TestConstants.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.facebook.csslayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generated constants used in {@link LayoutEngineTest}.
|
||||||
|
*/
|
||||||
|
public class TestConstants {
|
||||||
|
|
||||||
|
/** START_GENERATED **/
|
||||||
|
public static final float SMALL_WIDTH = 34.671875f;
|
||||||
|
public static final float SMALL_HEIGHT = 18f;
|
||||||
|
public static final float BIG_WIDTH = 172.421875f;
|
||||||
|
public static final float BIG_HEIGHT = 36f;
|
||||||
|
public static final float BIG_MIN_WIDTH = 100.453125f;
|
||||||
|
public static final String SMALL_TEXT = "small";
|
||||||
|
public static final String LONG_TEXT = "loooooooooong with space";
|
||||||
|
/** END_GENERATED **/
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
var layoutTestUtils = require('./Layout-test-utils.js');
|
var layoutTestUtils = require('./Layout-test-utils.js');
|
||||||
var computeLayout = require('./Layout.js');
|
var computeLayout = require('./Layout.js');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var JavaTranspiler = require('./JavaTranspiler.js');
|
||||||
|
|
||||||
var currentTest = '';
|
var currentTest = '';
|
||||||
var allTests = [];
|
var allTests = [];
|
||||||
@@ -223,6 +224,7 @@ function transpileAnnotatedJStoC(jsCode) {
|
|||||||
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
||||||
.replace(/ === /g, ' == ')
|
.replace(/ === /g, ' == ')
|
||||||
.replace(/\n /g, '\n')
|
.replace(/\n /g, '\n')
|
||||||
|
.replace(/\/\*\(c\)!([^*]+)\*\//g, '$1')
|
||||||
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
|
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
|
||||||
.split('\n').slice(1, -1).join('\n');
|
.split('\n').slice(1, -1).join('\n');
|
||||||
}
|
}
|
||||||
@@ -250,6 +252,10 @@ function generateFile(fileName, generatedContent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
generateFile(__dirname + '/__tests__/Layout-test.c', allTests.map(printLayout).join('\n\n'));
|
var allTestsInC = allTests.map(printLayout);
|
||||||
|
generateFile(__dirname + '/__tests__/Layout-test.c', allTestsInC.join('\n\n'));
|
||||||
generateFile(__dirname + '/Layout-test-utils.c', makeConstDefs());
|
generateFile(__dirname + '/Layout-test-utils.c', makeConstDefs());
|
||||||
generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayout.toString()));
|
generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayout.toString()));
|
||||||
|
generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(computeLayout.toString()));
|
||||||
|
generateFile(__dirname + '/java/tests/com/facebook/csslayout/TestConstants.java', JavaTranspiler.transpileCConstDefs(makeConstDefs()));
|
||||||
|
generateFile(__dirname + '/java/tests/com/facebook/csslayout/LayoutEngineTest.java', JavaTranspiler.transpileCTestsArray(allTestsInC));
|
||||||
|
Reference in New Issue
Block a user