Java
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,4 @@
|
||||
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
|
||||
|
||||
all: c test
|
||||
all: c c_test java java_test
|
||||
|
||||
c:
|
||||
@node ./src/transpile.js
|
||||
c: transpile_all
|
||||
|
||||
test:
|
||||
c_test: c
|
||||
@gcc -std=c99 -Werror -Wno-padded $(FILES) -lm && ./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:
|
||||
@gcc -ggdb $(FILES) -lm && lldb ./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
|
||||
// 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;
|
||||
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, 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
|
||||
// remaining space
|
||||
if (flexibleChildrenCount) {
|
||||
if (flexibleChildrenCount != 0) {
|
||||
float flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, 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++) {
|
||||
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]) &&
|
||||
!isPosDefined(child, 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
|
||||
if (isRowUndefined || isColumnUndefined) {
|
||||
var/*css_dim_t*/ measure_dim = node.style.measure(
|
||||
/*!node->context,*/
|
||||
/*(c)!node->context,*/
|
||||
width
|
||||
);
|
||||
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
|
||||
// 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;
|
||||
var/*css_flex_direction_t*/ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node.layout[dim[axis]]) &&
|
||||
!isDimDefined(child, 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
|
||||
// remaining space
|
||||
if (flexibleChildrenCount) {
|
||||
if (flexibleChildrenCount != 0) {
|
||||
var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
var/*css_flex_direction_t*/ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node.layout[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
@@ -567,7 +567,7 @@ var computeLayout = (function() {
|
||||
}
|
||||
}
|
||||
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]) &&
|
||||
!isPosDefined(child, 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 computeLayout = require('./Layout.js');
|
||||
var fs = require('fs');
|
||||
var JavaTranspiler = require('./JavaTranspiler.js');
|
||||
|
||||
var currentTest = '';
|
||||
var allTests = [];
|
||||
@@ -223,6 +224,7 @@ function transpileAnnotatedJStoC(jsCode) {
|
||||
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
||||
.replace(/ === /g, ' == ')
|
||||
.replace(/\n /g, '\n')
|
||||
.replace(/\/\*\(c\)!([^*]+)\*\//g, '$1')
|
||||
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
|
||||
.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.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