Merge pull request #129 from pragmatrix/csharp-fb-pr
C# Transpiler, API, and Tests
This commit is contained in:
175
src/CSharpTranspiler.js
Normal file
175
src/CSharpTranspiler.js
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __transpileToCSharpCommon(code) {
|
||||||
|
return code
|
||||||
|
.replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED')
|
||||||
|
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
|
||||||
|
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
|
||||||
|
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
|
||||||
|
.replace(/css_flex_direction_t/g, 'CSSFlexDirection')
|
||||||
|
.replace(/css_direction_t/g, 'CSSDirection')
|
||||||
|
.replace(/css_align_t/g, 'CSSAlign')
|
||||||
|
.replace(/css_justify_t/g, 'CSSJustify')
|
||||||
|
.replace(/css_dim_t/g, 'MeasureOutput')
|
||||||
|
.replace(/bool/g, 'boolean')
|
||||||
|
.replace(/style\[dim/g, 'style.dimensions[dim')
|
||||||
|
.replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]')
|
||||||
|
.replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]')
|
||||||
|
.replace(/layout\[dim/g, 'layout.dimensions[dim')
|
||||||
|
.replace(/layout\[pos/g, 'layout.position[pos')
|
||||||
|
.replace(/layout\[leading/g, 'layout.position[leading')
|
||||||
|
.replace(/layout\[trailing/g, 'layout.position[trailing')
|
||||||
|
.replace(/getPositionType\((.+?)\)/g, '$1.style.positionType')
|
||||||
|
.replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent')
|
||||||
|
.replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent')
|
||||||
|
.replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)')
|
||||||
|
.replace(/isDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] > 0.0\)')
|
||||||
|
.replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)')
|
||||||
|
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]')
|
||||||
|
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)')
|
||||||
|
.replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)')
|
||||||
|
.replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)')
|
||||||
|
.replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)')
|
||||||
|
.replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
|
||||||
|
.replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)')
|
||||||
|
.replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)')
|
||||||
|
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
|
||||||
|
.replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||||
|
.replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||||
|
.replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||||
|
.replace(/getTrailingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||||
|
.replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])')
|
||||||
|
.replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])')
|
||||||
|
.replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)')
|
||||||
|
.replace(/isUndefined\((.+?)\)/g, 'float.IsNaN\($1\)')
|
||||||
|
.replace(/\/\*\(c\)!([^*]+)\*\//g, '')
|
||||||
|
.replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||||
|
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||||
|
|
||||||
|
// additional case conversions
|
||||||
|
|
||||||
|
.replace(/(CSSConstants|CSSWrap|CSSJustify|CSSAlign|CSSPositionType)\.([_A-Z]+)/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + "." + constantToPascalCase(match2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function __transpileSingleTestToCSharp(code) {
|
||||||
|
return __transpileToCSharpCommon(code)
|
||||||
|
.replace(/CSS_DIRECTION_/g, 'CSSDirection.')
|
||||||
|
.replace(/CSS_FLEX_DIRECTION_/g, 'CSSFlexDirection.')
|
||||||
|
.replace(/CSS_WRAP/g, 'CSSWrap.WRAP')
|
||||||
|
.replace(/new_test_css_node/g, 'new TestCSSNode')
|
||||||
|
.replace( // style.position[CSS_TOP] => style.position[CSSLayout.POSITION_TOP]
|
||||||
|
/(style|layout)\.position\[CSS_(LEFT|TOP|RIGHT|BOTTOM)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + '.position[POSITION_' + match2 + ']';
|
||||||
|
})
|
||||||
|
.replace( // style.dimensions[CSS_WIDTH] => style.dimensions[CSSLayout.DIMENSION_WIDTH]
|
||||||
|
/(style|layout)\.dimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + '.dimensions[DIMENSION_' + match2 + ']';
|
||||||
|
})
|
||||||
|
.replace( // style.maxDimensions[CSS_WIDTH] => style.maxWidth
|
||||||
|
/(style|layout)\.maxDimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + '.max' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||||
|
})
|
||||||
|
.replace( // style.minDimensions[CSS_WIDTH] => style.minWidth
|
||||||
|
/(style|layout)\.minDimensions\[CSS_(WIDTH|HEIGHT)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + '.min' + match2.substr(0, 1).toUpperCase() + match2.substr(1).toLowerCase();
|
||||||
|
})
|
||||||
|
.replace( // style.margin[CSS_TOP] = 12.3 => style.margin[Spacing.TOP].set(12.3)
|
||||||
|
/style\.(margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT|START|END)\]\s+=\s+(-?[\.\d]+)/g,
|
||||||
|
function (str, match1, match2, match3) {
|
||||||
|
var propertyCap = match1.charAt(0).toUpperCase() + match1.slice(1);
|
||||||
|
return 'set' + propertyCap + '(Spacing.' + match2 + ', ' + match3 + ')';
|
||||||
|
})
|
||||||
|
.replace( // style.margin[CSS_TOP] => style.margin[Spacing.TOP]
|
||||||
|
/style\.(margin|border|padding)\[CSS_(TOP|BOTTOM|LEFT|RIGHT|START|END)\]/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return 'style.' + match1 + '.get(Spacing.' + match2 + ')';
|
||||||
|
})
|
||||||
|
.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);')
|
||||||
|
|
||||||
|
// additional case conversions
|
||||||
|
|
||||||
|
.replace(/(CSSWrap|CSSFlexDirection)\.([_A-Z]+)/g,
|
||||||
|
function (str, match1, match2) {
|
||||||
|
return match1 + "." + constantToPascalCase(match2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function indent(code) {
|
||||||
|
return code
|
||||||
|
.split('\n')
|
||||||
|
.map(function(line) { return ' ' + line; })
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function constantToPascalCase(str) {
|
||||||
|
return str[0] + str.substr(1)
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/_(.)/g,
|
||||||
|
function (_, m) { return m.toUpperCase(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
var CSharpTranspiler = {
|
||||||
|
transpileLayoutEngine: function(code) {
|
||||||
|
return indent(
|
||||||
|
__transpileToCSharpCommon(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(/node.children\[ii\]/g, 'node.getChildAt(ii)')
|
||||||
|
.replace(/fmaxf/g, 'Math.Max')
|
||||||
|
.replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages
|
||||||
|
.replace(/var\/\*([^\/]+)\*\//g, '$1')
|
||||||
|
.replace(/ === /g, ' == ')
|
||||||
|
.replace(/ !== /g, ' != ')
|
||||||
|
.replace(/\n {2}/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 readonly string $1 = $2;')
|
||||||
|
.replace(/#define\s+(\w+)\s+(.+)/g, 'public static readonly float $1 = $2f;'));
|
||||||
|
},
|
||||||
|
|
||||||
|
transpileCTestsArray: function(allTestsInC) {
|
||||||
|
var allTestsInCSharp = [];
|
||||||
|
for (var i = 0; i < allTestsInC.length; i++) {
|
||||||
|
allTestsInCSharp[i] =
|
||||||
|
" [Test]\n" +
|
||||||
|
" public void TestCase" + i + "()\n" +
|
||||||
|
__transpileSingleTestToCSharp(allTestsInC[i]);
|
||||||
|
}
|
||||||
|
return allTestsInCSharp.join('\n\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports = CSharpTranspiler;
|
||||||
|
}
|
13
src/csharp/.editorconfig
Normal file
13
src/csharp/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line=LF
|
||||||
|
|
||||||
|
[*.cs]
|
||||||
|
indent_style=space
|
||||||
|
indent_size=4
|
||||||
|
|
||||||
|
[*.js]
|
||||||
|
indent_style=space
|
||||||
|
indent_size=2
|
||||||
|
|
6
src/csharp/.gitignore
vendored
Normal file
6
src/csharp/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
/.vs/
|
||||||
|
*.user
|
||||||
|
*.nupkg
|
53
src/csharp/Facebook.CSSLayout.Tests/CSSNodeTest.cs
Normal file
53
src/csharp/Facebook.CSSLayout.Tests/CSSNodeTest.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout.Tests
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Tests for {@link CSSNode}.
|
||||||
|
*/
|
||||||
|
public class CSSNodeTest
|
||||||
|
{
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void testAddChildGetParent()
|
||||||
|
{
|
||||||
|
CSSNode parent = new CSSNode();
|
||||||
|
CSSNode child = new CSSNode();
|
||||||
|
|
||||||
|
Assert.Null(child.getParent());
|
||||||
|
Assert.AreEqual(0, parent.getChildCount());
|
||||||
|
|
||||||
|
parent.addChildAt(child, 0);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, parent.getChildCount());
|
||||||
|
Assert.AreEqual(child, parent.getChildAt(0));
|
||||||
|
Assert.AreEqual(parent, child.getParent());
|
||||||
|
|
||||||
|
parent.removeChildAt(0);
|
||||||
|
|
||||||
|
Assert.Null(child.getParent());
|
||||||
|
Assert.AreEqual(0, parent.getChildCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, ExpectedException(typeof(InvalidOperationException))]
|
||||||
|
public void testCannotAddChildToMultipleParents()
|
||||||
|
{
|
||||||
|
CSSNode parent1 = new CSSNode();
|
||||||
|
CSSNode parent2 = new CSSNode();
|
||||||
|
CSSNode child = new CSSNode();
|
||||||
|
|
||||||
|
parent1.addChildAt(child, 0);
|
||||||
|
parent2.addChildAt(child, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
src/csharp/Facebook.CSSLayout.Tests/Facebook.CSSLayout.Tests.csproj
Executable file
70
src/csharp/Facebook.CSSLayout.Tests/Facebook.CSSLayout.Tests.csproj
Executable file
@@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{E687C8FD-0A0D-450F-853D-EC301BE1C038}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Facebook.CSSLayout.Tests</RootNamespace>
|
||||||
|
<AssemblyName>Facebook.CSSLayout.Tests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="CSSNodeTest.cs" />
|
||||||
|
<Compile Include="LayoutCachingTest.cs" />
|
||||||
|
<Compile Include="LayoutEngineTest.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TestConstants.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Facebook.CSSLayout\Facebook.CSSLayout.csproj">
|
||||||
|
<Project>{d534fb4b-a7d4-4a29-96d3-f39a91a259bd}</Project>
|
||||||
|
<Name>Facebook.CSSLayout</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
240
src/csharp/Facebook.CSSLayout.Tests/LayoutCachingTest.cs
Normal file
240
src/csharp/Facebook.CSSLayout.Tests/LayoutCachingTest.cs
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout.Tests
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link LayoutEngine} and {@link CSSNode} to make sure layouts are only generated when
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
|
public class LayoutCachingTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private void assertTreeHasNewLayout(bool expectedHasNewLayout, CSSNode root)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expectedHasNewLayout, root.HasNewLayout);
|
||||||
|
|
||||||
|
for (int i = 0; i < root.getChildCount(); i++)
|
||||||
|
{
|
||||||
|
assertTreeHasNewLayout(expectedHasNewLayout, root.getChildAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markLayoutAppliedForTree(CSSNode root)
|
||||||
|
{
|
||||||
|
root.MarkLayoutSeen();
|
||||||
|
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();
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
assertTreeHasNewLayout(false, c0);
|
||||||
|
assertTreeHasNewLayout(false, c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void testInvalidatesCacheWhenChildAdded()
|
||||||
|
{
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
CSSNode c0c1 = new CSSNode();
|
||||||
|
CSSNode c1c0 = new CSSNode();
|
||||||
|
c0c1.Width = 200;
|
||||||
|
c0c1.Height = 200;
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
c0c0.addChildAt(c1c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
c0.addChildAt(c0c1, 1);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
Assert.True(c0.HasNewLayout);
|
||||||
|
Assert.True(c0c1.HasNewLayout);
|
||||||
|
|
||||||
|
Assert.True(c0c0.HasNewLayout);
|
||||||
|
Assert.True(c1.HasNewLayout);
|
||||||
|
|
||||||
|
Assert.False(c1c0.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();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
c1.AlignSelf = CSSAlign.Center;
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
Assert.True(c1.HasNewLayout);
|
||||||
|
|
||||||
|
Assert.True(c0.HasNewLayout);
|
||||||
|
Assert.False(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();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
c1.SetMargin(CSSSpacingType.Left, 10);
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
Assert.True(c1.HasNewLayout);
|
||||||
|
|
||||||
|
Assert.True(c0.HasNewLayout);
|
||||||
|
Assert.False(c0c0.HasNewLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void testInvalidatesFullTreeWhenParentWidthChanges()
|
||||||
|
{
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c0c0 = new CSSNode();
|
||||||
|
CSSNode c1c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c0.addChildAt(c0c0, 0);
|
||||||
|
c1.addChildAt(c1c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
c0.Height = 200;
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
Assert.True(c0.HasNewLayout);
|
||||||
|
Assert.True(c0c0.HasNewLayout);
|
||||||
|
|
||||||
|
Assert.True(c1.HasNewLayout);
|
||||||
|
Assert.False(c1c0.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.Width = 200;
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
root.Width = 200;
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
assertTreeHasNewLayout(false, c0);
|
||||||
|
assertTreeHasNewLayout(false, c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void testInvalidateCacheWhenHeightChangesPosition()
|
||||||
|
{
|
||||||
|
CSSNode root = new CSSNode();
|
||||||
|
CSSNode c0 = new CSSNode();
|
||||||
|
CSSNode c1 = new CSSNode();
|
||||||
|
CSSNode c1c0 = new CSSNode();
|
||||||
|
root.addChildAt(c0, 0);
|
||||||
|
root.addChildAt(c1, 1);
|
||||||
|
c1.addChildAt(c1c0, 0);
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
c0.Height = 100;
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
Assert.True(c0.HasNewLayout);
|
||||||
|
Assert.True(c1.HasNewLayout);
|
||||||
|
Assert.False(c1c0.HasNewLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void testInvalidatesOnNewMeasureFunction()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
markLayoutAppliedForTree(root);
|
||||||
|
|
||||||
|
c1.setMeasureFunction((node, width) => new MeasureOutput(100, 20));
|
||||||
|
|
||||||
|
root.calculateLayout();
|
||||||
|
|
||||||
|
Assert.True(root.HasNewLayout);
|
||||||
|
Assert.True(c1.HasNewLayout);
|
||||||
|
|
||||||
|
Assert.True(c0.HasNewLayout);
|
||||||
|
Assert.False(c0c0.HasNewLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8154
src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs
Normal file
8154
src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs
Normal file
File diff suppressed because it is too large
Load Diff
27
src/csharp/Facebook.CSSLayout.Tests/Properties/AssemblyInfo.cs
Executable file
27
src/csharp/Facebook.CSSLayout.Tests/Properties/AssemblyInfo.cs
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("Facebook.CSSLayout.Tests")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Facebook.CSSLayout.Tests")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Facebook 2015")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
[assembly: Guid("c186053a-741f-477d-b031-4d343fb20d1d")]
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
28
src/csharp/Facebook.CSSLayout.Tests/TestConstants.cs
Normal file
28
src/csharp/Facebook.CSSLayout.Tests/TestConstants.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout.Tests
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Generated constants used in {@link LayoutEngineTest}.
|
||||||
|
*/
|
||||||
|
public class TestConstants
|
||||||
|
{
|
||||||
|
|
||||||
|
/** START_GENERATED **/
|
||||||
|
public static readonly float SMALL_WIDTH = 35f;
|
||||||
|
public static readonly float SMALL_HEIGHT = 18f;
|
||||||
|
public static readonly float BIG_WIDTH = 172f;
|
||||||
|
public static readonly float BIG_HEIGHT = 36f;
|
||||||
|
public static readonly float BIG_MIN_WIDTH = 100f;
|
||||||
|
public static readonly string SMALL_TEXT = "small";
|
||||||
|
public static readonly string LONG_TEXT = "loooooooooong with space";
|
||||||
|
/** END_GENERATED **/
|
||||||
|
}
|
||||||
|
}
|
4
src/csharp/Facebook.CSSLayout.Tests/packages.config
Executable file
4
src/csharp/Facebook.CSSLayout.Tests/packages.config
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="NUnit" version="2.6.4" targetFramework="net45" />
|
||||||
|
</packages>
|
34
src/csharp/Facebook.CSSLayout.sln
Executable file
34
src/csharp/Facebook.CSSLayout.sln
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 14
|
||||||
|
VisualStudioVersion = 14.0.23107.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facebook.CSSLayout", "Facebook.CSSLayout\Facebook.CSSLayout.csproj", "{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facebook.CSSLayout.Tests", "Facebook.CSSLayout.Tests\Facebook.CSSLayout.Tests.csproj", "{E687C8FD-0A0D-450F-853D-EC301BE1C038}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{29A6932B-FDDC-4E8A-8895-7FD64CC47B7F}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\CSharpTranspiler.js = ..\CSharpTranspiler.js
|
||||||
|
..\JavaTranspiler.js = ..\JavaTranspiler.js
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E687C8FD-0A0D-450F-853D-EC301BE1C038}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
22
src/csharp/Facebook.CSSLayout/Assertions.cs
Executable file
22
src/csharp/Facebook.CSSLayout/Assertions.cs
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
static class Assertions
|
||||||
|
{
|
||||||
|
public static T assertNotNull<T>(T v) where T : class
|
||||||
|
{
|
||||||
|
Debug.Assert(v != null);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/csharp/Facebook.CSSLayout/CSSAlign.cs
Normal file
20
src/csharp/Facebook.CSSLayout/CSSAlign.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSAlign
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
FlexStart,
|
||||||
|
Center,
|
||||||
|
FlexEnd,
|
||||||
|
Stretch,
|
||||||
|
}
|
||||||
|
}
|
21
src/csharp/Facebook.CSSLayout/CSSConstants.cs
Normal file
21
src/csharp/Facebook.CSSLayout/CSSConstants.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public static class CSSConstants
|
||||||
|
{
|
||||||
|
public static readonly float Undefined = float.NaN;
|
||||||
|
|
||||||
|
public static bool IsUndefined(float value)
|
||||||
|
{
|
||||||
|
return float.IsNaN(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/csharp/Facebook.CSSLayout/CSSDirection.cs
Executable file
18
src/csharp/Facebook.CSSLayout/CSSDirection.cs
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSDirection
|
||||||
|
{
|
||||||
|
Inherit,
|
||||||
|
LTR,
|
||||||
|
RTL
|
||||||
|
}
|
||||||
|
}
|
19
src/csharp/Facebook.CSSLayout/CSSFlexDirection.cs
Normal file
19
src/csharp/Facebook.CSSLayout/CSSFlexDirection.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSFlexDirection
|
||||||
|
{
|
||||||
|
Column,
|
||||||
|
ColumnReverse,
|
||||||
|
Row,
|
||||||
|
RowReverse
|
||||||
|
}
|
||||||
|
}
|
20
src/csharp/Facebook.CSSLayout/CSSJustify.cs
Normal file
20
src/csharp/Facebook.CSSLayout/CSSJustify.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSJustify
|
||||||
|
{
|
||||||
|
FlexStart,
|
||||||
|
Center,
|
||||||
|
FlexEnd,
|
||||||
|
SpaceBetween,
|
||||||
|
SpaceAround
|
||||||
|
}
|
||||||
|
}
|
71
src/csharp/Facebook.CSSLayout/CSSLayout.cs
Normal file
71
src/csharp/Facebook.CSSLayout/CSSLayout.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CSSLayout
|
||||||
|
{
|
||||||
|
public const int POSITION_LEFT = 0;
|
||||||
|
public const int POSITION_TOP = 1;
|
||||||
|
public const int POSITION_RIGHT = 2;
|
||||||
|
public const int POSITION_BOTTOM = 3;
|
||||||
|
|
||||||
|
public const int DIMENSION_WIDTH = 0;
|
||||||
|
public const int DIMENSION_HEIGHT = 1;
|
||||||
|
|
||||||
|
public float[] position = new float[4];
|
||||||
|
public float[] dimensions = new float[2];
|
||||||
|
public CSSDirection direction = CSSDirection.LTR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void resetResult()
|
||||||
|
{
|
||||||
|
FillArray(position, 0);
|
||||||
|
FillArray(dimensions, CSSConstants.Undefined);
|
||||||
|
|
||||||
|
direction = CSSDirection.LTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copy(CSSLayout layout)
|
||||||
|
{
|
||||||
|
position[POSITION_LEFT] = layout.position[POSITION_LEFT];
|
||||||
|
position[POSITION_TOP] = layout.position[POSITION_TOP];
|
||||||
|
position[POSITION_RIGHT] = layout.position[POSITION_RIGHT];
|
||||||
|
position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM];
|
||||||
|
dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH];
|
||||||
|
dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT];
|
||||||
|
direction = layout.direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "layout: {" +
|
||||||
|
"left: " + position[POSITION_LEFT] + ", " +
|
||||||
|
"top: " + position[POSITION_TOP] + ", " +
|
||||||
|
"width: " + dimensions[DIMENSION_WIDTH] + ", " +
|
||||||
|
"height: " + dimensions[DIMENSION_HEIGHT] + ", " +
|
||||||
|
"direction: " + direction +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FillArray<T>(T[] array, T value)
|
||||||
|
{
|
||||||
|
for (var i = 0; i != array.Length; ++i)
|
||||||
|
array[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs
Executable file
26
src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context for holding values local to a given instance of layout computation.
|
||||||
|
*
|
||||||
|
* This is necessary for making layout thread-safe. A separate instance should
|
||||||
|
* be used when {@link CSSNode#calculateLayout} is called concurrently on
|
||||||
|
* different node hierarchies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sealed class CSSLayoutContext
|
||||||
|
{
|
||||||
|
/*package*/
|
||||||
|
public MeasureOutput measureOutput = new MeasureOutput();
|
||||||
|
}
|
||||||
|
}
|
505
src/csharp/Facebook.CSSLayout/CSSNode.cs
Normal file
505
src/csharp/Facebook.CSSLayout/CSSNode.cs
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using static Facebook.CSSLayout.CSSLayout;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Should measure the given node and put the result in the given MeasureOutput.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public delegate MeasureOutput MeasureFunction(CSSNode node, float width);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
{
|
||||||
|
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 #MarkLayoutSeen()} was called.
|
||||||
|
*/
|
||||||
|
HAS_NEW_LAYOUT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #layout} is valid for the node's properties and this layout has been marked as
|
||||||
|
* having been seen.
|
||||||
|
*/
|
||||||
|
UP_TO_DATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly CSSStyle style = new CSSStyle();
|
||||||
|
internal readonly CSSLayout layout = new CSSLayout();
|
||||||
|
internal readonly CachedCSSLayout lastLayout = new CachedCSSLayout();
|
||||||
|
|
||||||
|
internal int lineIndex = 0;
|
||||||
|
internal /*package*/ CSSNode nextAbsoluteChild;
|
||||||
|
internal /*package*/ CSSNode nextFlexChild;
|
||||||
|
|
||||||
|
// 4 is kinda arbitrary, but the default of 10 seems really high for an average View.
|
||||||
|
readonly List<CSSNode> mChildren = new List<CSSNode>(4);
|
||||||
|
[Nullable] CSSNode mParent;
|
||||||
|
[Nullable] MeasureFunction mMeasureFunction = null;
|
||||||
|
LayoutState mLayoutState = LayoutState.DIRTY;
|
||||||
|
|
||||||
|
public int ChildCount
|
||||||
|
=> mChildren.Count;
|
||||||
|
|
||||||
|
public CSSNode this[int i]
|
||||||
|
=> mChildren[i];
|
||||||
|
|
||||||
|
public IEnumerable<CSSNode> Children
|
||||||
|
=> mChildren;
|
||||||
|
|
||||||
|
public void AddChild(CSSNode child)
|
||||||
|
{
|
||||||
|
InsertChild(ChildCount, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertChild(int i, CSSNode child)
|
||||||
|
{
|
||||||
|
if (child.mParent != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Child already has a parent, it must be removed first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mChildren.Insert(i, child);
|
||||||
|
child.mParent = this;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveChildAt(int i)
|
||||||
|
{
|
||||||
|
mChildren[i].mParent = null;
|
||||||
|
mChildren.RemoveAt(i);
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSNode Parent
|
||||||
|
{
|
||||||
|
[return: Nullable]
|
||||||
|
get
|
||||||
|
{ return mParent; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the index of the given child, or -1 if the child doesn't exist in this node.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public int IndexOf(CSSNode child)
|
||||||
|
{
|
||||||
|
return mChildren.IndexOf(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MeasureFunction MeasureFunction
|
||||||
|
{
|
||||||
|
get { return mMeasureFunction; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!valuesEqual(mMeasureFunction, value))
|
||||||
|
{
|
||||||
|
mMeasureFunction = value;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMeasureDefined
|
||||||
|
=> mMeasureFunction != null;
|
||||||
|
|
||||||
|
internal MeasureOutput measure(MeasureOutput measureOutput, float width)
|
||||||
|
{
|
||||||
|
if (!IsMeasureDefined)
|
||||||
|
{
|
||||||
|
throw new Exception("Measure function isn't defined!");
|
||||||
|
}
|
||||||
|
return Assertions.assertNotNull(mMeasureFunction)(this, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the actual layout and saves the results in {@link #layout}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void CalculateLayout()
|
||||||
|
{
|
||||||
|
layout.resetResult();
|
||||||
|
LayoutEngine.layoutNode(DummyLayoutContext, this, CSSConstants.Undefined, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly CSSLayoutContext DummyLayoutContext = new CSSLayoutContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link LayoutState#DIRTY}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public bool IsDirty
|
||||||
|
=> mLayoutState == LayoutState.DIRTY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link LayoutState#HAS_NEW_LAYOUT}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public bool HasNewLayout
|
||||||
|
=> mLayoutState == LayoutState.HAS_NEW_LAYOUT;
|
||||||
|
|
||||||
|
internal protected void dirty()
|
||||||
|
{
|
||||||
|
if (mLayoutState == LayoutState.DIRTY)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (mLayoutState == LayoutState.HAS_NEW_LAYOUT)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Previous layout was ignored! MarkLayoutSeen() never called");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLayoutState = LayoutState.DIRTY;
|
||||||
|
|
||||||
|
if (mParent != null)
|
||||||
|
{
|
||||||
|
mParent.dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void markHasNewLayout()
|
||||||
|
{
|
||||||
|
mLayoutState = LayoutState.HAS_NEW_LAYOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the node that the current values in {@link #layout} have been seen. 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 MarkLayoutSeen()
|
||||||
|
{
|
||||||
|
if (!HasNewLayout)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Expected node to have a new layout to be seen!");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLayoutState = LayoutState.UP_TO_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toStringWithIndentation(StringBuilder result, int level)
|
||||||
|
{
|
||||||
|
// Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
|
||||||
|
StringBuilder indentation = new StringBuilder();
|
||||||
|
for (int i = 0; i < level; ++i)
|
||||||
|
{
|
||||||
|
indentation.Append("__");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(indentation.ToString());
|
||||||
|
result.Append(layout.ToString());
|
||||||
|
|
||||||
|
if (ChildCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(", children: [\n");
|
||||||
|
for (var i = 0; i < ChildCount; i++)
|
||||||
|
{
|
||||||
|
this[i].toStringWithIndentation(result, level + 1);
|
||||||
|
result.Append("\n");
|
||||||
|
}
|
||||||
|
result.Append(indentation + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
this.toStringWithIndentation(sb, 0);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool valuesEqual(float f1, float f2)
|
||||||
|
{
|
||||||
|
return FloatUtil.floatsEqual(f1, f2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool valuesEqual<T>([Nullable] T o1, [Nullable] T o2)
|
||||||
|
{
|
||||||
|
if (o1 == null)
|
||||||
|
{
|
||||||
|
return o2 == null;
|
||||||
|
}
|
||||||
|
return o1.Equals(o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSDirection Direction
|
||||||
|
{
|
||||||
|
get { return style.direction; }
|
||||||
|
set { updateDiscreteValue(ref style.direction, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSFlexDirection FlexDirection
|
||||||
|
{
|
||||||
|
get { return style.flexDirection; }
|
||||||
|
set { updateDiscreteValue(ref style.flexDirection, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSJustify JustifyContent
|
||||||
|
{
|
||||||
|
get { return style.justifyContent; }
|
||||||
|
set { updateDiscreteValue(ref style.justifyContent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSAlign AlignContent
|
||||||
|
{
|
||||||
|
get { return style.alignContent; }
|
||||||
|
set { updateDiscreteValue(ref style.alignContent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSAlign AlignItems
|
||||||
|
{
|
||||||
|
get { return style.alignItems; }
|
||||||
|
set { updateDiscreteValue(ref style.alignItems, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSAlign AlignSelf
|
||||||
|
{
|
||||||
|
get { return style.alignSelf; }
|
||||||
|
set { updateDiscreteValue(ref style.alignSelf, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSPositionType PositionType
|
||||||
|
{
|
||||||
|
get { return style.positionType; }
|
||||||
|
set { updateDiscreteValue(ref style.positionType, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSSWrap Wrap
|
||||||
|
{
|
||||||
|
get { return style.flexWrap; }
|
||||||
|
set { updateDiscreteValue(ref style.flexWrap, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Flex
|
||||||
|
{
|
||||||
|
get { return style.flex; }
|
||||||
|
set { updateFloatValue(ref style.flex, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMargin(CSSSpacingType spacingType, float margin)
|
||||||
|
{
|
||||||
|
if (style.margin.set((int)spacingType, margin))
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetMargin(CSSSpacingType spacingType)
|
||||||
|
{
|
||||||
|
return style.margin.getRaw((int)spacingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPadding(CSSSpacingType spacingType, float padding)
|
||||||
|
{
|
||||||
|
if (style.padding.set((int)spacingType, padding))
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetPadding(CSSSpacingType spacingType)
|
||||||
|
{
|
||||||
|
return style.padding.getRaw((int)spacingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBorder(CSSSpacingType spacingType, float border)
|
||||||
|
{
|
||||||
|
if (style.border.set((int)spacingType, border))
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetBorder(CSSSpacingType spacingType)
|
||||||
|
{
|
||||||
|
return style.border.getRaw((int)spacingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float PositionTop
|
||||||
|
{
|
||||||
|
get { return style.position[POSITION_TOP]; }
|
||||||
|
set { updateFloatValue(ref style.position[POSITION_TOP], value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float PositionBottom
|
||||||
|
{
|
||||||
|
get { return style.position[POSITION_BOTTOM]; }
|
||||||
|
set { updateFloatValue(ref style.position[POSITION_BOTTOM], value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float PositionLeft
|
||||||
|
{
|
||||||
|
get { return style.position[POSITION_LEFT]; }
|
||||||
|
set { updateFloatValue(ref style.position[POSITION_LEFT], value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float PositionRight
|
||||||
|
{
|
||||||
|
get { return style.position[POSITION_RIGHT]; }
|
||||||
|
set { updateFloatValue(ref style.position[POSITION_RIGHT], value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Width
|
||||||
|
{
|
||||||
|
get { return style.dimensions[DIMENSION_WIDTH]; }
|
||||||
|
set { updateFloatValue(ref style.dimensions[DIMENSION_HEIGHT], value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Height
|
||||||
|
{
|
||||||
|
get { return style.dimensions[DIMENSION_HEIGHT]; }
|
||||||
|
set { updateFloatValue(ref style.dimensions[DIMENSION_HEIGHT], value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float MinWidth
|
||||||
|
{
|
||||||
|
get { return style.minWidth; }
|
||||||
|
set { updateFloatValue(ref style.minWidth, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float MinHeight
|
||||||
|
{
|
||||||
|
get { return style.minHeight; }
|
||||||
|
set { updateFloatValue(ref style.minHeight, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float MaxWidth
|
||||||
|
{
|
||||||
|
get { return style.maxWidth; }
|
||||||
|
set { updateFloatValue(ref style.maxWidth, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float MaxHeight
|
||||||
|
{
|
||||||
|
get { return style.maxHeight; }
|
||||||
|
set { updateFloatValue(ref style.maxHeight, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public float LayoutX => layout.position[POSITION_LEFT];
|
||||||
|
public float LayoutY => layout.position[POSITION_TOP];
|
||||||
|
public float LayoutWidth => layout.dimensions[DIMENSION_WIDTH];
|
||||||
|
public float LayoutHeight => layout.dimensions[DIMENSION_HEIGHT];
|
||||||
|
public CSSDirection LayoutDirection => layout.direction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a default padding (left/top/right/bottom) for this node.
|
||||||
|
*/
|
||||||
|
public void SetDefaultPadding(CSSSpacingType spacingType, float padding)
|
||||||
|
{
|
||||||
|
if (style.padding.setDefault((int)spacingType, padding))
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDiscreteValue<ValueT>(ref ValueT valueRef, ValueT newValue)
|
||||||
|
{
|
||||||
|
if (valuesEqual(valueRef, newValue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
valueRef = newValue;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateFloatValue(ref float valueRef, float newValue)
|
||||||
|
{
|
||||||
|
if (valuesEqual(valueRef, newValue))
|
||||||
|
return;
|
||||||
|
valueRef = newValue;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CSSNodeExtensions
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Explicitly mark this node as dirty.
|
||||||
|
|
||||||
|
Calling this function is required when the measure function points to the same instance,
|
||||||
|
but changes its behavior.
|
||||||
|
|
||||||
|
For all other property changes, the node is automatically marked dirty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static void MarkDirty(this CSSNode node)
|
||||||
|
{
|
||||||
|
node.dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class CSSNodeExtensionsInternal
|
||||||
|
{
|
||||||
|
public static CSSNode getParent(this CSSNode node)
|
||||||
|
{
|
||||||
|
return node.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getChildCount(this CSSNode node)
|
||||||
|
{
|
||||||
|
return node.ChildCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CSSNode getChildAt(this CSSNode node, int i)
|
||||||
|
{
|
||||||
|
return node[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addChildAt(this CSSNode node, CSSNode child, int i)
|
||||||
|
{
|
||||||
|
node.InsertChild(i, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeChildAt(this CSSNode node, int i)
|
||||||
|
{
|
||||||
|
node.RemoveChildAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setMeasureFunction(this CSSNode node, MeasureFunction measureFunction)
|
||||||
|
{
|
||||||
|
node.MeasureFunction = measureFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void calculateLayout(this CSSNode node)
|
||||||
|
{
|
||||||
|
node.CalculateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool isDirty(this CSSNode node)
|
||||||
|
{
|
||||||
|
return node.IsDirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setMargin(this CSSNode node, int spacingType, float margin)
|
||||||
|
{
|
||||||
|
node.SetMargin((CSSSpacingType)spacingType, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPadding(this CSSNode node, int spacingType, float padding)
|
||||||
|
{
|
||||||
|
node.SetPadding((CSSSpacingType)spacingType, padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setBorder(this CSSNode node, int spacingType, float border)
|
||||||
|
{
|
||||||
|
node.SetBorder((CSSSpacingType)spacingType, border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/csharp/Facebook.CSSLayout/CSSPositionType.cs
Normal file
17
src/csharp/Facebook.CSSLayout/CSSPositionType.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSPositionType
|
||||||
|
{
|
||||||
|
Relative,
|
||||||
|
Absolute
|
||||||
|
}
|
||||||
|
}
|
24
src/csharp/Facebook.CSSLayout/CSSSpacingType.cs
Executable file
24
src/csharp/Facebook.CSSLayout/CSSSpacingType.cs
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSSpacingType
|
||||||
|
{
|
||||||
|
Left = 0,
|
||||||
|
Top = 1,
|
||||||
|
Right = 2,
|
||||||
|
Bottom = 3,
|
||||||
|
Vertical = 4,
|
||||||
|
Horizontal = 5,
|
||||||
|
Start = 6,
|
||||||
|
End = 7,
|
||||||
|
All = 8
|
||||||
|
}
|
||||||
|
}
|
49
src/csharp/Facebook.CSSLayout/CSSStyle.cs
Normal file
49
src/csharp/Facebook.CSSLayout/CSSStyle.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The CSS style definition for a {@link CSSNode}.
|
||||||
|
*/
|
||||||
|
sealed class CSSStyle
|
||||||
|
{
|
||||||
|
public CSSDirection direction = CSSDirection.Inherit;
|
||||||
|
public CSSFlexDirection flexDirection = CSSFlexDirection.Column;
|
||||||
|
public CSSJustify justifyContent = CSSJustify.FlexStart;
|
||||||
|
public CSSAlign alignContent = CSSAlign.FlexStart;
|
||||||
|
public CSSAlign alignItems = CSSAlign.Stretch;
|
||||||
|
public CSSAlign alignSelf = CSSAlign.Auto;
|
||||||
|
public CSSPositionType positionType = CSSPositionType.Relative;
|
||||||
|
public CSSWrap flexWrap = CSSWrap.NoWrap;
|
||||||
|
public float flex;
|
||||||
|
|
||||||
|
public Spacing margin = new Spacing();
|
||||||
|
public Spacing padding = new Spacing();
|
||||||
|
public Spacing border = new Spacing();
|
||||||
|
|
||||||
|
public float[] position = {
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
public float[] dimensions = {
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
public float minWidth = CSSConstants.Undefined;
|
||||||
|
public float minHeight = CSSConstants.Undefined;
|
||||||
|
|
||||||
|
public float maxWidth = CSSConstants.Undefined;
|
||||||
|
public float maxHeight = CSSConstants.Undefined;
|
||||||
|
}
|
||||||
|
}
|
16
src/csharp/Facebook.CSSLayout/CSSWrap.cs
Normal file
16
src/csharp/Facebook.CSSLayout/CSSWrap.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
public enum CSSWrap
|
||||||
|
{
|
||||||
|
NoWrap,
|
||||||
|
Wrap
|
||||||
|
}
|
||||||
|
}
|
24
src/csharp/Facebook.CSSLayout/CachedCSSLayout.cs
Normal file
24
src/csharp/Facebook.CSSLayout/CachedCSSLayout.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CachedCSSLayout : CSSLayout
|
||||||
|
{
|
||||||
|
public float requestedWidth = CSSConstants.Undefined;
|
||||||
|
public float requestedHeight = CSSConstants.Undefined;
|
||||||
|
public float parentMaxWidth = CSSConstants.Undefined;
|
||||||
|
}
|
||||||
|
}
|
69
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj
Executable file
69
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{D534FB4B-A7D4-4A29-96D3-F39A91A259BD}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Facebook.CSSLayout</RootNamespace>
|
||||||
|
<AssemblyName>Facebook.CSSLayout</AssemblyName>
|
||||||
|
<DefaultLanguage>en-US</DefaultLanguage>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Assertions.cs" />
|
||||||
|
<Compile Include="CachedCSSLayout.cs" />
|
||||||
|
<Compile Include="CSSAlign.cs" />
|
||||||
|
<Compile Include="CSSConstants.cs" />
|
||||||
|
<Compile Include="CSSDirection.cs" />
|
||||||
|
<Compile Include="CSSFlexDirection.cs" />
|
||||||
|
<Compile Include="CSSJustify.cs" />
|
||||||
|
<Compile Include="CSSLayout.cs" />
|
||||||
|
<Compile Include="CSSLayoutContext.cs" />
|
||||||
|
<Compile Include="CSSNode.cs" />
|
||||||
|
<Compile Include="CSSPositionType.cs" />
|
||||||
|
<Compile Include="CSSStyle.cs" />
|
||||||
|
<Compile Include="CSSWrap.cs" />
|
||||||
|
<Compile Include="FloatUtil.cs" />
|
||||||
|
<Compile Include="LayoutEngine.cs" />
|
||||||
|
<Compile Include="MeasureOutput.cs" />
|
||||||
|
<Compile Include="NullableAttribute.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Spacing.cs" />
|
||||||
|
<Compile Include="CSSSpacingType.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
17
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.nuspec
Executable file
17
src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.nuspec
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<package >
|
||||||
|
<metadata>
|
||||||
|
<id>$id$</id>
|
||||||
|
<version>$version$</version>
|
||||||
|
<title>$title$</title>
|
||||||
|
<authors>$author$</authors>
|
||||||
|
<owners>$author$</owners>
|
||||||
|
<licenseUrl>https://github.com/facebook/css-layout/blob/master/LICENSE</licenseUrl>
|
||||||
|
<projectUrl>https://github.com/facebook/css-layout</projectUrl>
|
||||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
|
<description>$description$</description>
|
||||||
|
<releaseNotes></releaseNotes>
|
||||||
|
<copyright>Copyright 2015 Facebook</copyright>
|
||||||
|
<tags>flexbox flex-box css layout css-layout facebook</tags>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
27
src/csharp/Facebook.CSSLayout/FloatUtil.cs
Normal file
27
src/csharp/Facebook.CSSLayout/FloatUtil.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
static class FloatUtil
|
||||||
|
{
|
||||||
|
const float Epsilon = .00001f;
|
||||||
|
|
||||||
|
public static bool floatsEqual(float f1, float f2)
|
||||||
|
{
|
||||||
|
if (float.IsNaN(f1) || float.IsNaN(f2))
|
||||||
|
{
|
||||||
|
return float.IsNaN(f1) && float.IsNaN(f2);
|
||||||
|
}
|
||||||
|
return Math.Abs(f2 - f1) < Epsilon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
928
src/csharp/Facebook.CSSLayout/LayoutEngine.cs
Normal file
928
src/csharp/Facebook.CSSLayout/LayoutEngine.cs
Normal file
@@ -0,0 +1,928 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using boolean = System.Boolean;
|
||||||
|
using static Facebook.CSSLayout.CSSLayout;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates layouts based on CSS style. See {@link #layoutNode(CSSNode, float)}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static class LayoutEngine
|
||||||
|
{
|
||||||
|
const int CSS_FLEX_DIRECTION_COLUMN =
|
||||||
|
(int)CSSFlexDirection.Column;
|
||||||
|
const int CSS_FLEX_DIRECTION_COLUMN_REVERSE =
|
||||||
|
(int)CSSFlexDirection.ColumnReverse;
|
||||||
|
const int CSS_FLEX_DIRECTION_ROW =
|
||||||
|
(int)CSSFlexDirection.Row;
|
||||||
|
const int CSS_FLEX_DIRECTION_ROW_REVERSE =
|
||||||
|
(int)CSSFlexDirection.RowReverse;
|
||||||
|
|
||||||
|
const int CSS_POSITION_RELATIVE = (int)CSSPositionType.Relative;
|
||||||
|
const int CSS_POSITION_ABSOLUTE = (int)CSSPositionType.Absolute;
|
||||||
|
|
||||||
|
private static readonly int[] leading = {
|
||||||
|
POSITION_TOP,
|
||||||
|
POSITION_BOTTOM,
|
||||||
|
POSITION_LEFT,
|
||||||
|
POSITION_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly int[] trailing = {
|
||||||
|
POSITION_BOTTOM,
|
||||||
|
POSITION_TOP,
|
||||||
|
POSITION_RIGHT,
|
||||||
|
POSITION_LEFT,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly int[] pos = {
|
||||||
|
POSITION_TOP,
|
||||||
|
POSITION_BOTTOM,
|
||||||
|
POSITION_LEFT,
|
||||||
|
POSITION_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly int[] dim = {
|
||||||
|
DIMENSION_HEIGHT,
|
||||||
|
DIMENSION_HEIGHT,
|
||||||
|
DIMENSION_WIDTH,
|
||||||
|
DIMENSION_WIDTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly int[] leadingSpacing = {
|
||||||
|
Spacing.TOP,
|
||||||
|
Spacing.BOTTOM,
|
||||||
|
Spacing.START,
|
||||||
|
Spacing.START
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly int[] trailingSpacing = {
|
||||||
|
Spacing.BOTTOM,
|
||||||
|
Spacing.TOP,
|
||||||
|
Spacing.END,
|
||||||
|
Spacing.END
|
||||||
|
};
|
||||||
|
|
||||||
|
private static float boundAxis(CSSNode node, int axis, float value)
|
||||||
|
{
|
||||||
|
float min = CSSConstants.Undefined;
|
||||||
|
float max = CSSConstants.Undefined;
|
||||||
|
|
||||||
|
if (axis == CSS_FLEX_DIRECTION_COLUMN || axis == CSS_FLEX_DIRECTION_COLUMN_REVERSE)
|
||||||
|
{
|
||||||
|
min = node.style.minHeight;
|
||||||
|
max = node.style.maxHeight;
|
||||||
|
}
|
||||||
|
else if (axis == CSS_FLEX_DIRECTION_ROW || axis == CSS_FLEX_DIRECTION_ROW_REVERSE)
|
||||||
|
{
|
||||||
|
min = node.style.minWidth;
|
||||||
|
max = node.style.maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
float boundValue = value;
|
||||||
|
|
||||||
|
if (!float.IsNaN(max) && max >= 0.0 && boundValue > max)
|
||||||
|
{
|
||||||
|
boundValue = max;
|
||||||
|
}
|
||||||
|
if (!float.IsNaN(min) && min >= 0.0 && boundValue < min)
|
||||||
|
{
|
||||||
|
boundValue = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setDimensionFromStyle(CSSNode node, int axis)
|
||||||
|
{
|
||||||
|
// The parent already computed us a width or height. We just skip it
|
||||||
|
if (!float.IsNaN(node.layout.dimensions[dim[axis]]))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We only run if there's a width or height defined
|
||||||
|
if (float.IsNaN(node.style.dimensions[dim[axis]]) ||
|
||||||
|
node.style.dimensions[dim[axis]] <= 0.0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dimensions can never be smaller than the padding and border
|
||||||
|
float maxLayoutDimension = Math.Max(
|
||||||
|
boundAxis(node, axis, node.style.dimensions[dim[axis]]),
|
||||||
|
node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) +
|
||||||
|
node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) +
|
||||||
|
node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) +
|
||||||
|
node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]));
|
||||||
|
node.layout.dimensions[dim[axis]] = maxLayoutDimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getRelativePosition(CSSNode node, int axis)
|
||||||
|
{
|
||||||
|
float lead = node.style.position[leading[axis]];
|
||||||
|
if (!float.IsNaN(lead))
|
||||||
|
{
|
||||||
|
return lead;
|
||||||
|
}
|
||||||
|
|
||||||
|
float trailingPos = node.style.position[trailing[axis]];
|
||||||
|
return float.IsNaN(trailingPos) ? 0 : -trailingPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int resolveAxis(int axis, CSSDirection direction)
|
||||||
|
{
|
||||||
|
if (direction == CSSDirection.RTL)
|
||||||
|
{
|
||||||
|
if (axis == CSS_FLEX_DIRECTION_ROW)
|
||||||
|
{
|
||||||
|
return CSS_FLEX_DIRECTION_ROW_REVERSE;
|
||||||
|
}
|
||||||
|
else if (axis == CSS_FLEX_DIRECTION_ROW_REVERSE)
|
||||||
|
{
|
||||||
|
return CSS_FLEX_DIRECTION_ROW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CSSDirection resolveDirection(CSSNode node, CSSDirection? parentDirection)
|
||||||
|
{
|
||||||
|
CSSDirection direction = node.style.direction;
|
||||||
|
if (direction == CSSDirection.Inherit)
|
||||||
|
{
|
||||||
|
direction = (parentDirection == null ? CSSDirection.LTR : parentDirection.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getFlexDirection(CSSNode node)
|
||||||
|
{
|
||||||
|
return (int)node.style.flexDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getCrossFlexDirection(int axis, CSSDirection direction)
|
||||||
|
{
|
||||||
|
if (axis == CSS_FLEX_DIRECTION_COLUMN || axis == CSS_FLEX_DIRECTION_COLUMN_REVERSE)
|
||||||
|
{
|
||||||
|
return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return CSS_FLEX_DIRECTION_COLUMN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CSSAlign getAlignItem(CSSNode node, CSSNode child)
|
||||||
|
{
|
||||||
|
if (child.style.alignSelf != CSSAlign.Auto)
|
||||||
|
{
|
||||||
|
return child.style.alignSelf;
|
||||||
|
}
|
||||||
|
return node.style.alignItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isMeasureDefined(CSSNode node)
|
||||||
|
{
|
||||||
|
return node.IsMeasureDefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean needsRelayout(CSSNode node, float parentMaxWidth)
|
||||||
|
{
|
||||||
|
return node.isDirty() ||
|
||||||
|
!FloatUtil.floatsEqual(
|
||||||
|
node.lastLayout.requestedHeight,
|
||||||
|
node.layout.dimensions[DIMENSION_HEIGHT]) ||
|
||||||
|
!FloatUtil.floatsEqual(
|
||||||
|
node.lastLayout.requestedWidth,
|
||||||
|
node.layout.dimensions[DIMENSION_WIDTH]) ||
|
||||||
|
!FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void layoutNode(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, CSSDirection? parentDirection)
|
||||||
|
{
|
||||||
|
if (needsRelayout(node, parentMaxWidth))
|
||||||
|
{
|
||||||
|
node.lastLayout.requestedWidth = node.layout.dimensions[DIMENSION_WIDTH];
|
||||||
|
node.lastLayout.requestedHeight = node.layout.dimensions[DIMENSION_HEIGHT];
|
||||||
|
node.lastLayout.parentMaxWidth = parentMaxWidth;
|
||||||
|
|
||||||
|
layoutNodeImpl(layoutContext, node, parentMaxWidth, parentDirection);
|
||||||
|
node.lastLayout.copy(node.layout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node.layout.copy(node.lastLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.markHasNewLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, CSSDirection? parentDirection)
|
||||||
|
{
|
||||||
|
var childCount_ = node.getChildCount();
|
||||||
|
for (int i_ = 0; i_ < childCount_; i_++)
|
||||||
|
{
|
||||||
|
node.getChildAt(i_).layout.resetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** START_GENERATED **/
|
||||||
|
|
||||||
|
CSSDirection direction = resolveDirection(node, parentDirection);
|
||||||
|
int mainAxis = resolveAxis(getFlexDirection(node), direction);
|
||||||
|
int crossAxis = getCrossFlexDirection(mainAxis, direction);
|
||||||
|
int resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
|
||||||
|
|
||||||
|
// Handle width and height style attributes
|
||||||
|
setDimensionFromStyle(node, mainAxis);
|
||||||
|
setDimensionFromStyle(node, crossAxis);
|
||||||
|
|
||||||
|
// Set the resolved resolution in the node's layout
|
||||||
|
node.layout.direction = direction;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
node.layout.position[leading[mainAxis]] += node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
|
||||||
|
getRelativePosition(node, mainAxis);
|
||||||
|
node.layout.position[trailing[mainAxis]] += node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) +
|
||||||
|
getRelativePosition(node, mainAxis);
|
||||||
|
node.layout.position[leading[crossAxis]] += node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
|
||||||
|
getRelativePosition(node, crossAxis);
|
||||||
|
node.layout.position[trailing[crossAxis]] += node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) +
|
||||||
|
getRelativePosition(node, crossAxis);
|
||||||
|
|
||||||
|
// Inline immutable values from the target node to avoid excessive method
|
||||||
|
// invocations during the layout calculation.
|
||||||
|
int childCount = node.getChildCount();
|
||||||
|
float paddingAndBorderAxisResolvedRow = ((node.style.padding.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.border.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis])) + (node.style.padding.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]) + node.style.border.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])));
|
||||||
|
|
||||||
|
if (isMeasureDefined(node)) {
|
||||||
|
boolean isResolvedRowDimDefined = !float.IsNaN(node.layout.dimensions[dim[resolvedRowAxis]]);
|
||||||
|
|
||||||
|
float width = CSSConstants.Undefined;
|
||||||
|
if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] > 0.0)) {
|
||||||
|
width = node.style.dimensions[DIMENSION_WIDTH];
|
||||||
|
} else if (isResolvedRowDimDefined) {
|
||||||
|
width = node.layout.dimensions[dim[resolvedRowAxis]];
|
||||||
|
} else {
|
||||||
|
width = parentMaxWidth -
|
||||||
|
(node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]));
|
||||||
|
}
|
||||||
|
width -= paddingAndBorderAxisResolvedRow;
|
||||||
|
|
||||||
|
// 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 = !(!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] > 0.0) && !isResolvedRowDimDefined;
|
||||||
|
boolean isColumnUndefined = !(!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] > 0.0) &&
|
||||||
|
float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
|
||||||
|
|
||||||
|
// Let's not measure the text if we already know both dimensions
|
||||||
|
if (isRowUndefined || isColumnUndefined) {
|
||||||
|
MeasureOutput measureDim = node.measure(
|
||||||
|
|
||||||
|
layoutContext.measureOutput,
|
||||||
|
width
|
||||||
|
);
|
||||||
|
if (isRowUndefined) {
|
||||||
|
node.layout.dimensions[DIMENSION_WIDTH] = measureDim.width +
|
||||||
|
paddingAndBorderAxisResolvedRow;
|
||||||
|
}
|
||||||
|
if (isColumnUndefined) {
|
||||||
|
node.layout.dimensions[DIMENSION_HEIGHT] = measureDim.height +
|
||||||
|
((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (childCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.Wrap);
|
||||||
|
|
||||||
|
CSSJustify justifyContent = node.style.justifyContent;
|
||||||
|
|
||||||
|
float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]));
|
||||||
|
float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]));
|
||||||
|
float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
|
||||||
|
float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));
|
||||||
|
|
||||||
|
boolean isMainDimDefined = !float.IsNaN(node.layout.dimensions[dim[mainAxis]]);
|
||||||
|
boolean isCrossDimDefined = !float.IsNaN(node.layout.dimensions[dim[crossAxis]]);
|
||||||
|
boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int ii;
|
||||||
|
CSSNode child;
|
||||||
|
int axis;
|
||||||
|
|
||||||
|
CSSNode firstAbsoluteChild = null;
|
||||||
|
CSSNode currentAbsoluteChild = null;
|
||||||
|
|
||||||
|
float definedMainDim = CSSConstants.Undefined;
|
||||||
|
if (isMainDimDefined) {
|
||||||
|
definedMainDim = node.layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to execute the next two loops one per line with flex-wrap
|
||||||
|
int startLine = 0;
|
||||||
|
int endLine = 0;
|
||||||
|
// int nextOffset = 0;
|
||||||
|
int alreadyComputedNextLayout = 0;
|
||||||
|
// We aggregate the total dimensions of the container in those two variables
|
||||||
|
float linesCrossDim = 0;
|
||||||
|
float linesMainDim = 0;
|
||||||
|
int linesCount = 0;
|
||||||
|
while (endLine < childCount) {
|
||||||
|
// <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;
|
||||||
|
|
||||||
|
// Use the line loop to position children in the main axis for as long
|
||||||
|
// as they are using a simple stacking behaviour. Children that are
|
||||||
|
// immediately stacked in the initial loop will not be touched again
|
||||||
|
// in <Loop C>.
|
||||||
|
boolean isSimpleStackMain =
|
||||||
|
(isMainDimDefined && justifyContent == CSSJustify.FlexStart) ||
|
||||||
|
(!isMainDimDefined && justifyContent != CSSJustify.Center);
|
||||||
|
int firstComplexMain = (isSimpleStackMain ? childCount : startLine);
|
||||||
|
|
||||||
|
// Use the initial line loop to position children in the cross axis for
|
||||||
|
// as long as they are relatively positioned with alignment STRETCH or
|
||||||
|
// FLEX_START. Children that are immediately stacked in the initial loop
|
||||||
|
// will not be touched again in <Loop D>.
|
||||||
|
boolean isSimpleStackCross = true;
|
||||||
|
int firstComplexCross = childCount;
|
||||||
|
|
||||||
|
CSSNode firstFlexChild = null;
|
||||||
|
CSSNode currentFlexChild = null;
|
||||||
|
|
||||||
|
float mainDim = leadingPaddingAndBorderMain;
|
||||||
|
float crossDim = 0;
|
||||||
|
|
||||||
|
float maxWidth;
|
||||||
|
for (i = startLine; i < childCount; ++i) {
|
||||||
|
child = node.getChildAt(i);
|
||||||
|
child.lineIndex = linesCount;
|
||||||
|
|
||||||
|
child.nextAbsoluteChild = null;
|
||||||
|
child.nextFlexChild = null;
|
||||||
|
|
||||||
|
CSSAlign alignItem = getAlignItem(node, child);
|
||||||
|
|
||||||
|
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||||
|
// we call the recursive layout pass
|
||||||
|
if (alignItem == CSSAlign.Stretch &&
|
||||||
|
child.style.positionType == CSSPositionType.Relative &&
|
||||||
|
isCrossDimDefined &&
|
||||||
|
!(!float.IsNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] > 0.0)) {
|
||||||
|
child.layout.dimensions[dim[crossAxis]] = Math.Max(
|
||||||
|
boundAxis(child, crossAxis, node.layout.dimensions[dim[crossAxis]] -
|
||||||
|
paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
|
||||||
|
);
|
||||||
|
} else if (child.style.positionType == CSSPositionType.Absolute) {
|
||||||
|
// Store a private linked list of absolutely positioned children
|
||||||
|
// so that we can efficiently traverse them later.
|
||||||
|
if (firstAbsoluteChild == null) {
|
||||||
|
firstAbsoluteChild = child;
|
||||||
|
}
|
||||||
|
if (currentAbsoluteChild != null) {
|
||||||
|
currentAbsoluteChild.nextAbsoluteChild = child;
|
||||||
|
}
|
||||||
|
currentAbsoluteChild = child;
|
||||||
|
|
||||||
|
// 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 (ii = 0; ii < 2; ii++) {
|
||||||
|
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
|
if (!float.IsNaN(node.layout.dimensions[dim[axis]]) &&
|
||||||
|
!(!float.IsNaN(child.style.dimensions[dim[axis]]) && child.style.dimensions[dim[axis]] > 0.0) &&
|
||||||
|
!float.IsNaN(child.style.position[leading[axis]]) &&
|
||||||
|
!float.IsNaN(child.style.position[trailing[axis]])) {
|
||||||
|
child.layout.dimensions[dim[axis]] = Math.Max(
|
||||||
|
boundAxis(child, axis, node.layout.dimensions[dim[axis]] -
|
||||||
|
((node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) -
|
||||||
|
(child.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
|
||||||
|
(float.IsNaN(child.style.position[leading[axis]]) ? 0 : child.style.position[leading[axis]]) -
|
||||||
|
(float.IsNaN(child.style.position[trailing[axis]]) ? 0 : child.style.position[trailing[axis]])),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
((child.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (child.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + child.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float nextContentDim = 0;
|
||||||
|
|
||||||
|
// It only makes sense to consider a child flexible if we have a computed
|
||||||
|
// dimension for the node.
|
||||||
|
if (isMainDimDefined && (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0)) {
|
||||||
|
flexibleChildrenCount++;
|
||||||
|
totalFlexible += child.style.flex;
|
||||||
|
|
||||||
|
// Store a private linked list of flexible children so that we can
|
||||||
|
// efficiently traverse them later.
|
||||||
|
if (firstFlexChild == null) {
|
||||||
|
firstFlexChild = child;
|
||||||
|
}
|
||||||
|
if (currentFlexChild != null) {
|
||||||
|
currentFlexChild.nextFlexChild = child;
|
||||||
|
}
|
||||||
|
currentFlexChild = 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, which represents
|
||||||
|
// the smallest possible size for the child, to compute the remaining
|
||||||
|
// available space.
|
||||||
|
nextContentDim = ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) +
|
||||||
|
(child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
maxWidth = CSSConstants.Undefined;
|
||||||
|
if (!isMainRowDirection) {
|
||||||
|
if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] > 0.0)) {
|
||||||
|
maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
|
||||||
|
paddingAndBorderAxisResolvedRow;
|
||||||
|
} else {
|
||||||
|
maxWidth = parentMaxWidth -
|
||||||
|
(node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
|
||||||
|
paddingAndBorderAxisResolvedRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the main recursive call. We layout non flexible children.
|
||||||
|
if (alreadyComputedNextLayout == 0) {
|
||||||
|
layoutNode(layoutContext, child, maxWidth, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Absolute positioned elements do not take part of the layout, so we
|
||||||
|
// don't use them to compute mainContentDim
|
||||||
|
if (child.style.positionType == CSSPositionType.Relative) {
|
||||||
|
nonFlexibleChildrenCount++;
|
||||||
|
// At this point we know the final size and margin of the element.
|
||||||
|
nextContentDim = (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The element we are about to add would make us go to the next line
|
||||||
|
if (isNodeFlexWrap &&
|
||||||
|
isMainDimDefined &&
|
||||||
|
mainContentDim + nextContentDim > definedMainDim &&
|
||||||
|
// If there's only one element, then it's bigger than the content
|
||||||
|
// and needs its own line
|
||||||
|
i != startLine) {
|
||||||
|
nonFlexibleChildrenCount--;
|
||||||
|
alreadyComputedNextLayout = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable simple stacking in the main axis for the current line as
|
||||||
|
// we found a non-trivial child. The remaining children will be laid out
|
||||||
|
// in <Loop C>.
|
||||||
|
if (isSimpleStackMain &&
|
||||||
|
(child.style.positionType != CSSPositionType.Relative || (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0))) {
|
||||||
|
isSimpleStackMain = false;
|
||||||
|
firstComplexMain = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable simple stacking in the cross axis for the current line as
|
||||||
|
// we found a non-trivial child. The remaining children will be laid out
|
||||||
|
// in <Loop D>.
|
||||||
|
if (isSimpleStackCross &&
|
||||||
|
(child.style.positionType != CSSPositionType.Relative ||
|
||||||
|
(alignItem != CSSAlign.Stretch && alignItem != CSSAlign.FlexStart) ||
|
||||||
|
float.IsNaN(child.layout.dimensions[dim[crossAxis]]))) {
|
||||||
|
isSimpleStackCross = false;
|
||||||
|
firstComplexCross = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSimpleStackMain) {
|
||||||
|
child.layout.position[pos[mainAxis]] += mainDim;
|
||||||
|
if (isMainDimDefined) {
|
||||||
|
child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
|
||||||
|
}
|
||||||
|
|
||||||
|
mainDim += (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
|
||||||
|
crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSimpleStackCross) {
|
||||||
|
child.layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
|
||||||
|
if (isCrossDimDefined) {
|
||||||
|
child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyComputedNextLayout = 0;
|
||||||
|
mainContentDim += nextContentDim;
|
||||||
|
endLine = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <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;
|
||||||
|
|
||||||
|
// The remaining available space that needs to be allocated
|
||||||
|
float remainingMainDim = 0;
|
||||||
|
if (isMainDimDefined) {
|
||||||
|
remainingMainDim = definedMainDim - mainContentDim;
|
||||||
|
} else {
|
||||||
|
remainingMainDim = Math.Max(mainContentDim, 0) - mainContentDim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are flexible children in the mix, they are going to fill the
|
||||||
|
// remaining space
|
||||||
|
if (flexibleChildrenCount != 0) {
|
||||||
|
float flexibleMainDim = remainingMainDim / totalFlexible;
|
||||||
|
float baseMainDim;
|
||||||
|
float boundMainDim;
|
||||||
|
|
||||||
|
// If the flex share of remaining space doesn't meet min/max bounds,
|
||||||
|
// remove this child from flex calculations.
|
||||||
|
currentFlexChild = firstFlexChild;
|
||||||
|
while (currentFlexChild != null) {
|
||||||
|
baseMainDim = flexibleMainDim * currentFlexChild.style.flex +
|
||||||
|
((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
|
||||||
|
boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
|
||||||
|
|
||||||
|
if (baseMainDim != boundMainDim) {
|
||||||
|
remainingMainDim -= boundMainDim;
|
||||||
|
totalFlexible -= currentFlexChild.style.flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFlexChild = currentFlexChild.nextFlexChild;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFlexChild = firstFlexChild;
|
||||||
|
while (currentFlexChild != null) {
|
||||||
|
// At this point we know the final size of the element in the main
|
||||||
|
// dimension
|
||||||
|
currentFlexChild.layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
|
||||||
|
flexibleMainDim * currentFlexChild.style.flex +
|
||||||
|
((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))
|
||||||
|
);
|
||||||
|
|
||||||
|
maxWidth = CSSConstants.Undefined;
|
||||||
|
if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] > 0.0)) {
|
||||||
|
maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
|
||||||
|
paddingAndBorderAxisResolvedRow;
|
||||||
|
} else if (!isMainRowDirection) {
|
||||||
|
maxWidth = parentMaxWidth -
|
||||||
|
(node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
|
||||||
|
paddingAndBorderAxisResolvedRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And we recursively call the layout algorithm for this child
|
||||||
|
layoutNode(layoutContext, currentFlexChild, maxWidth, direction);
|
||||||
|
|
||||||
|
child = currentFlexChild;
|
||||||
|
currentFlexChild = currentFlexChild.nextFlexChild;
|
||||||
|
child.nextFlexChild = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use justifyContent to figure out how to allocate the remaining
|
||||||
|
// space available
|
||||||
|
} else if (justifyContent != CSSJustify.FlexStart) {
|
||||||
|
if (justifyContent == CSSJustify.Center) {
|
||||||
|
leadingMainDim = remainingMainDim / 2;
|
||||||
|
} else if (justifyContent == CSSJustify.FlexEnd) {
|
||||||
|
leadingMainDim = remainingMainDim;
|
||||||
|
} else if (justifyContent == CSSJustify.SpaceBetween) {
|
||||||
|
remainingMainDim = Math.Max(remainingMainDim, 0);
|
||||||
|
if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
|
||||||
|
betweenMainDim = remainingMainDim /
|
||||||
|
(flexibleChildrenCount + nonFlexibleChildrenCount - 1);
|
||||||
|
} else {
|
||||||
|
betweenMainDim = 0;
|
||||||
|
}
|
||||||
|
} else if (justifyContent == CSSJustify.SpaceAround) {
|
||||||
|
// 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!
|
||||||
|
mainDim += leadingMainDim;
|
||||||
|
|
||||||
|
for (i = firstComplexMain; i < endLine; ++i) {
|
||||||
|
child = node.getChildAt(i);
|
||||||
|
|
||||||
|
if (child.style.positionType == CSSPositionType.Absolute &&
|
||||||
|
!float.IsNaN(child.style.position[leading[mainAxis]])) {
|
||||||
|
// In case the child is position absolute and has left/top being
|
||||||
|
// defined, we override the position to whatever the user said
|
||||||
|
// (and margin/border).
|
||||||
|
child.layout.position[pos[mainAxis]] = (float.IsNaN(child.style.position[leading[mainAxis]]) ? 0 : child.style.position[leading[mainAxis]]) +
|
||||||
|
node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
|
||||||
|
child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]);
|
||||||
|
} else {
|
||||||
|
// If the child is position absolute (without top/left) or relative,
|
||||||
|
// we put it at the current accumulated offset.
|
||||||
|
child.layout.position[pos[mainAxis]] += mainDim;
|
||||||
|
|
||||||
|
// Define the trailing position accordingly.
|
||||||
|
if (isMainDimDefined) {
|
||||||
|
child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (child.style.positionType == CSSPositionType.Relative) {
|
||||||
|
// The main dimension is the sum of all the elements dimension plus
|
||||||
|
// the spacing.
|
||||||
|
mainDim += betweenMainDim + (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[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, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float containerCrossAxis = node.layout.dimensions[dim[crossAxis]];
|
||||||
|
if (!isCrossDimDefined) {
|
||||||
|
containerCrossAxis = 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
|
||||||
|
boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
|
||||||
|
paddingAndBorderAxisCross
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop D> Position elements in the cross axis
|
||||||
|
for (i = firstComplexCross; i < endLine; ++i) {
|
||||||
|
child = node.getChildAt(i);
|
||||||
|
|
||||||
|
if (child.style.positionType == CSSPositionType.Absolute &&
|
||||||
|
!float.IsNaN(child.style.position[leading[crossAxis]])) {
|
||||||
|
// In case the child is absolutely positionned and has a
|
||||||
|
// top/left/bottom/right being set, we override all the previously
|
||||||
|
// computed positions to set it correctly.
|
||||||
|
child.layout.position[pos[crossAxis]] = (float.IsNaN(child.style.position[leading[crossAxis]]) ? 0 : child.style.position[leading[crossAxis]]) +
|
||||||
|
node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
|
||||||
|
child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
float leadingCrossDim = leadingPaddingAndBorderCross;
|
||||||
|
|
||||||
|
// For a relative children, we're either using alignItems (parent) or
|
||||||
|
// alignSelf (child) in order to determine the position in the cross axis
|
||||||
|
if (child.style.positionType == CSSPositionType.Relative) {
|
||||||
|
CSSAlign alignItem = getAlignItem(node, child);
|
||||||
|
if (alignItem == CSSAlign.Stretch) {
|
||||||
|
// You can only stretch if the dimension has not already been set
|
||||||
|
// previously.
|
||||||
|
if (float.IsNaN(child.layout.dimensions[dim[crossAxis]])) {
|
||||||
|
child.layout.dimensions[dim[crossAxis]] = Math.Max(
|
||||||
|
boundAxis(child, crossAxis, containerCrossAxis -
|
||||||
|
paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (alignItem != CSSAlign.FlexStart) {
|
||||||
|
// The remaining space between the parent dimensions+padding and child
|
||||||
|
// dimensions+margin.
|
||||||
|
float remainingCrossDim = containerCrossAxis -
|
||||||
|
paddingAndBorderAxisCross - (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]));
|
||||||
|
|
||||||
|
if (alignItem == CSSAlign.Center) {
|
||||||
|
leadingCrossDim += remainingCrossDim / 2;
|
||||||
|
} else { // CSSAlign.FlexEnd
|
||||||
|
leadingCrossDim += remainingCrossDim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And we apply the position
|
||||||
|
child.layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
|
||||||
|
|
||||||
|
// Define the trailing position accordingly.
|
||||||
|
if (isCrossDimDefined) {
|
||||||
|
child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linesCrossDim += crossDim;
|
||||||
|
linesMainDim = Math.Max(linesMainDim, mainDim);
|
||||||
|
linesCount += 1;
|
||||||
|
startLine = endLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop E>
|
||||||
|
//
|
||||||
|
// Note(prenaux): More than one line, we need to layout the crossAxis
|
||||||
|
// according to alignContent.
|
||||||
|
//
|
||||||
|
// Note that we could probably remove <Loop D> and handle the one line case
|
||||||
|
// here too, but for the moment this is safer since it won't interfere with
|
||||||
|
// previously working code.
|
||||||
|
//
|
||||||
|
// See specs:
|
||||||
|
// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
|
||||||
|
// section 9.4
|
||||||
|
//
|
||||||
|
if (linesCount > 1 && isCrossDimDefined) {
|
||||||
|
float nodeCrossAxisInnerSize = node.layout.dimensions[dim[crossAxis]] -
|
||||||
|
paddingAndBorderAxisCross;
|
||||||
|
float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
|
||||||
|
|
||||||
|
float crossDimLead = 0;
|
||||||
|
float currentLead = leadingPaddingAndBorderCross;
|
||||||
|
|
||||||
|
CSSAlign alignContent = node.style.alignContent;
|
||||||
|
if (alignContent == CSSAlign.FlexEnd) {
|
||||||
|
currentLead += remainingAlignContentDim;
|
||||||
|
} else if (alignContent == CSSAlign.Center) {
|
||||||
|
currentLead += remainingAlignContentDim / 2;
|
||||||
|
} else if (alignContent == CSSAlign.Stretch) {
|
||||||
|
if (nodeCrossAxisInnerSize > linesCrossDim) {
|
||||||
|
crossDimLead = (remainingAlignContentDim / linesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int endIndex = 0;
|
||||||
|
for (i = 0; i < linesCount; ++i) {
|
||||||
|
int startIndex = endIndex;
|
||||||
|
|
||||||
|
// compute the line's height and find the endIndex
|
||||||
|
float lineHeight = 0;
|
||||||
|
for (ii = startIndex; ii < childCount; ++ii) {
|
||||||
|
child = node.getChildAt(ii);
|
||||||
|
if (child.style.positionType != CSSPositionType.Relative) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (child.lineIndex != i) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!float.IsNaN(child.layout.dimensions[dim[crossAxis]])) {
|
||||||
|
lineHeight = Math.Max(
|
||||||
|
lineHeight,
|
||||||
|
child.layout.dimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endIndex = ii;
|
||||||
|
lineHeight += crossDimLead;
|
||||||
|
|
||||||
|
for (ii = startIndex; ii < endIndex; ++ii) {
|
||||||
|
child = node.getChildAt(ii);
|
||||||
|
if (child.style.positionType != CSSPositionType.Relative) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSSAlign alignContentAlignItem = getAlignItem(node, child);
|
||||||
|
if (alignContentAlignItem == CSSAlign.FlexStart) {
|
||||||
|
child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
|
||||||
|
} else if (alignContentAlignItem == CSSAlign.FlexEnd) {
|
||||||
|
child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.dimensions[dim[crossAxis]];
|
||||||
|
} else if (alignContentAlignItem == CSSAlign.Center) {
|
||||||
|
float childHeight = child.layout.dimensions[dim[crossAxis]];
|
||||||
|
child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
|
||||||
|
} else if (alignContentAlignItem == CSSAlign.Stretch) {
|
||||||
|
child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
|
||||||
|
// TODO(prenaux): Correctly set the height of items with undefined
|
||||||
|
// (auto) crossAxis dimension.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLead += lineHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean needsMainTrailingPos = false;
|
||||||
|
boolean needsCrossTrailingPos = false;
|
||||||
|
|
||||||
|
// 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 (!isMainDimDefined) {
|
||||||
|
node.layout.dimensions[dim[mainAxis]] = Math.Max(
|
||||||
|
// We're missing the last padding at this point to get the final
|
||||||
|
// dimension
|
||||||
|
boundAxis(node, mainAxis, linesMainDim + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))),
|
||||||
|
// We can never assign a width smaller than the padding and borders
|
||||||
|
paddingAndBorderAxisMain
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||||
|
mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||||
|
needsMainTrailingPos = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isCrossDimDefined) {
|
||||||
|
node.layout.dimensions[dim[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
|
||||||
|
boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
|
||||||
|
paddingAndBorderAxisCross
|
||||||
|
);
|
||||||
|
|
||||||
|
if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
||||||
|
crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
||||||
|
needsCrossTrailingPos = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop F> Set trailing position if necessary
|
||||||
|
if (needsMainTrailingPos || needsCrossTrailingPos) {
|
||||||
|
for (i = 0; i < childCount; ++i) {
|
||||||
|
child = node.getChildAt(i);
|
||||||
|
|
||||||
|
if (needsMainTrailingPos) {
|
||||||
|
child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsCrossTrailingPos) {
|
||||||
|
child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Loop G> Calculate dimensions for absolutely positioned elements
|
||||||
|
currentAbsoluteChild = firstAbsoluteChild;
|
||||||
|
while (currentAbsoluteChild != null) {
|
||||||
|
// 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 (ii = 0; ii < 2; ii++) {
|
||||||
|
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||||
|
|
||||||
|
if (!float.IsNaN(node.layout.dimensions[dim[axis]]) &&
|
||||||
|
!(!float.IsNaN(currentAbsoluteChild.style.dimensions[dim[axis]]) && currentAbsoluteChild.style.dimensions[dim[axis]] > 0.0) &&
|
||||||
|
!float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) &&
|
||||||
|
!float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]])) {
|
||||||
|
currentAbsoluteChild.layout.dimensions[dim[axis]] = Math.Max(
|
||||||
|
boundAxis(currentAbsoluteChild, axis, node.layout.dimensions[dim[axis]] -
|
||||||
|
(node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])) -
|
||||||
|
(currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
|
||||||
|
(float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) ? 0 : currentAbsoluteChild.style.position[leading[axis]]) -
|
||||||
|
(float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]])
|
||||||
|
),
|
||||||
|
// You never want to go smaller than padding
|
||||||
|
((currentAbsoluteChild.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (currentAbsoluteChild.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + currentAbsoluteChild.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) &&
|
||||||
|
!!float.IsNaN(currentAbsoluteChild.style.position[leading[axis]])) {
|
||||||
|
currentAbsoluteChild.layout.position[leading[axis]] =
|
||||||
|
node.layout.dimensions[dim[axis]] -
|
||||||
|
currentAbsoluteChild.layout.dimensions[dim[axis]] -
|
||||||
|
(float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
child = currentAbsoluteChild;
|
||||||
|
currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
|
||||||
|
child.nextAbsoluteChild = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** END_GENERATED **/
|
||||||
|
}
|
||||||
|
}
|
29
src/csharp/Facebook.CSSLayout/MeasureOutput.cs
Normal file
29
src/csharp/Facebook.CSSLayout/MeasureOutput.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* POJO to hold the output of the measure function.
|
||||||
|
*/
|
||||||
|
public struct MeasureOutput
|
||||||
|
{
|
||||||
|
public MeasureOutput(float width, float height)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly float Width;
|
||||||
|
public readonly float Height;
|
||||||
|
|
||||||
|
internal float width => Width;
|
||||||
|
internal float height => Height;
|
||||||
|
}
|
||||||
|
}
|
22
src/csharp/Facebook.CSSLayout/NullableAttribute.cs
Executable file
22
src/csharp/Facebook.CSSLayout/NullableAttribute.cs
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is here to preserve the @nullable attribute of the original Java API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
|
||||||
|
sealed class NullableAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
27
src/csharp/Facebook.CSSLayout/Properties/AssemblyInfo.cs
Executable file
27
src/csharp/Facebook.CSSLayout/Properties/AssemblyInfo.cs
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Resources;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("Facebook.CSSLayout")]
|
||||||
|
[assembly: AssemblyDescription("A subset of CSS's flexbox layout algorithm and box model.")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Facebook")]
|
||||||
|
[assembly: AssemblyProduct("Facebook.CSSLayout")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2015 Facebook")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Facebook.CSSLayout.Tests")]
|
234
src/csharp/Facebook.CSSLayout/Spacing.cs
Normal file
234
src/csharp/Facebook.CSSLayout/Spacing.cs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Facebook.CSSLayout
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Class representing CSS spacing (padding, margin, and borders). This is mostly necessary to
|
||||||
|
* properly implement interactions and updates for properties like margin, marginLeft, and
|
||||||
|
* marginHorizontal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sealed class Spacing
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Spacing type that represents the left direction. E.g. {@code marginLeft}.
|
||||||
|
*/
|
||||||
|
internal const int LEFT = (int)CSSSpacingType.Left;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents the top direction. E.g. {@code marginTop}.
|
||||||
|
*/
|
||||||
|
internal const int TOP = (int)CSSSpacingType.Top;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents the right direction. E.g. {@code marginRight}.
|
||||||
|
*/
|
||||||
|
internal const int RIGHT = (int)CSSSpacingType.Right;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents the bottom direction. E.g. {@code marginBottom}.
|
||||||
|
*/
|
||||||
|
internal const int BOTTOM = (int)CSSSpacingType.Bottom;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}.
|
||||||
|
*/
|
||||||
|
internal const int VERTICAL = (int)CSSSpacingType.Vertical;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents horizontal direction (left and right). E.g.
|
||||||
|
* {@code marginHorizontal}.
|
||||||
|
*/
|
||||||
|
internal const int HORIZONTAL = (int)CSSSpacingType.Horizontal;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents start direction e.g. left in left-to-right, right in right-to-left.
|
||||||
|
*/
|
||||||
|
internal const int START = (int)CSSSpacingType.Start;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left.
|
||||||
|
*/
|
||||||
|
internal const int END = (int)CSSSpacingType.End;
|
||||||
|
/**
|
||||||
|
* Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}.
|
||||||
|
*/
|
||||||
|
internal const int ALL = (int)CSSSpacingType.All;
|
||||||
|
|
||||||
|
static readonly int[] sFlagsMap = {
|
||||||
|
1, /*LEFT*/
|
||||||
|
2, /*TOP*/
|
||||||
|
4, /*RIGHT*/
|
||||||
|
8, /*BOTTOM*/
|
||||||
|
16, /*VERTICAL*/
|
||||||
|
32, /*HORIZONTAL*/
|
||||||
|
64, /*START*/
|
||||||
|
128, /*END*/
|
||||||
|
256 /*ALL*/
|
||||||
|
};
|
||||||
|
|
||||||
|
float[] mSpacing = newFullSpacingArray();
|
||||||
|
[Nullable] float[] mDefaultSpacing = null;
|
||||||
|
int mValueFlags = 0;
|
||||||
|
bool mHasAliasesSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a spacing value.
|
||||||
|
*
|
||||||
|
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM},
|
||||||
|
* {@link #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
|
||||||
|
* @param value the value for this direction
|
||||||
|
* @return {@code true} if the spacing has changed, or {@code false} if the same value was already
|
||||||
|
* set
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal bool set(int spacingType, float value)
|
||||||
|
{
|
||||||
|
if (!FloatUtil.floatsEqual(mSpacing[spacingType], value))
|
||||||
|
{
|
||||||
|
mSpacing[spacingType] = value;
|
||||||
|
|
||||||
|
if (CSSConstants.IsUndefined(value))
|
||||||
|
{
|
||||||
|
mValueFlags &= ~sFlagsMap[spacingType];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mValueFlags |= sFlagsMap[spacingType];
|
||||||
|
}
|
||||||
|
|
||||||
|
mHasAliasesSet =
|
||||||
|
(mValueFlags & sFlagsMap[ALL]) != 0 ||
|
||||||
|
(mValueFlags & sFlagsMap[VERTICAL]) != 0 ||
|
||||||
|
(mValueFlags & sFlagsMap[HORIZONTAL]) != 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a default spacing value. This is used as a fallback when no spacing has been set for a
|
||||||
|
* particular direction.
|
||||||
|
*
|
||||||
|
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
|
||||||
|
* @param value the default value for this direction
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal bool setDefault(int spacingType, float value)
|
||||||
|
{
|
||||||
|
if (mDefaultSpacing == null)
|
||||||
|
mDefaultSpacing = newSpacingResultArray();
|
||||||
|
|
||||||
|
if (!FloatUtil.floatsEqual(mDefaultSpacing[spacingType], value))
|
||||||
|
{
|
||||||
|
mDefaultSpacing[spacingType] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the spacing for a direction. This takes into account any default values that have been set.
|
||||||
|
*
|
||||||
|
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal float get(int spacingType)
|
||||||
|
{
|
||||||
|
float defaultValue =
|
||||||
|
(mDefaultSpacing != null)
|
||||||
|
? mDefaultSpacing[spacingType]
|
||||||
|
: (spacingType == START || spacingType == END ? CSSConstants.Undefined : 0);
|
||||||
|
|
||||||
|
if (mValueFlags == 0)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mValueFlags & sFlagsMap[spacingType]) != 0)
|
||||||
|
{
|
||||||
|
return mSpacing[spacingType];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHasAliasesSet)
|
||||||
|
{
|
||||||
|
int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL;
|
||||||
|
if ((mValueFlags & sFlagsMap[secondType]) != 0)
|
||||||
|
{
|
||||||
|
return mSpacing[secondType];
|
||||||
|
}
|
||||||
|
else if ((mValueFlags & sFlagsMap[ALL]) != 0)
|
||||||
|
{
|
||||||
|
return mSpacing[ALL];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raw value (that was set using {@link #set(int, float)}), without taking into account
|
||||||
|
* any default values.
|
||||||
|
*
|
||||||
|
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM},
|
||||||
|
* {@link #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal float getRaw(int spacingType)
|
||||||
|
{
|
||||||
|
return mSpacing[spacingType];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to get start value and fallback to given type if not defined. This is used privately
|
||||||
|
* by the layout engine as a more efficient way to fetch direction-aware values by
|
||||||
|
* avoid extra method invocations.
|
||||||
|
*/
|
||||||
|
internal float getWithFallback(int spacingType, int fallbackType)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(mValueFlags & sFlagsMap[spacingType]) != 0
|
||||||
|
? mSpacing[spacingType]
|
||||||
|
: get(fallbackType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float[] newFullSpacingArray()
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static float[] newSpacingResultArray()
|
||||||
|
{
|
||||||
|
return newSpacingResultArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float[] newSpacingResultArray(float defaultValue)
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
defaultValue,
|
||||||
|
defaultValue,
|
||||||
|
defaultValue,
|
||||||
|
defaultValue,
|
||||||
|
defaultValue,
|
||||||
|
defaultValue,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
CSSConstants.Undefined,
|
||||||
|
defaultValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/csharp/Makefile
Normal file
38
src/csharp/Makefile
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
MSB=msbuild.exe /m /verbosity:m /nologo
|
||||||
|
NUGET=nuget.exe
|
||||||
|
NUNITC=nunit-console.exe
|
||||||
|
|
||||||
|
VER=1.0.0
|
||||||
|
NAME=Facebook.CSSLayout
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: test
|
||||||
|
|
||||||
|
.PHONY: distribute
|
||||||
|
distribute: package release-package
|
||||||
|
|
||||||
|
.PHONY: package
|
||||||
|
package: conf=Release
|
||||||
|
package: build
|
||||||
|
cd ${NAME} && ${NUGET} pack ${NAME}.csproj -Version ${VER} -Prop Configuration=${conf}
|
||||||
|
|
||||||
|
.PHONY: release-package
|
||||||
|
release-package:
|
||||||
|
cd ${NAME} && nuget push ${NAME}.${VER}.nupkg
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: build-debug
|
||||||
|
cd ${NAME}.Tests/bin/Debug && ${NUNITC} Facebook.CSSLayout.Tests.dll
|
||||||
|
|
||||||
|
.PHONY: build-debug
|
||||||
|
build-debug: conf=Debug
|
||||||
|
build-debug: build
|
||||||
|
|
||||||
|
.PHONY: build-release
|
||||||
|
build-release: conf=Release
|
||||||
|
build-release: build
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
${MSB} ${NAME}.sln /p:Configuration=${conf} /t:"Facebook_CSSLayout:Rebuild;Facebook_CSSLayout_Tests:Rebuild"
|
||||||
|
|
@@ -11,6 +11,7 @@ var layoutTestUtils = require('./Layout-test-utils.js');
|
|||||||
var computeLayout = require('./Layout.js').computeLayout;
|
var computeLayout = require('./Layout.js').computeLayout;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var JavaTranspiler = require('./JavaTranspiler.js');
|
var JavaTranspiler = require('./JavaTranspiler.js');
|
||||||
|
var CSharpTranspiler = require('./CSharpTranspiler.js');
|
||||||
|
|
||||||
var currentTest = '';
|
var currentTest = '';
|
||||||
var allTests = [];
|
var allTests = [];
|
||||||
@@ -310,3 +311,6 @@ generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayout.toSt
|
|||||||
generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(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/TestConstants.java', JavaTranspiler.transpileCConstDefs(makeConstDefs()));
|
||||||
generateFile(__dirname + '/java/tests/com/facebook/csslayout/LayoutEngineTest.java', JavaTranspiler.transpileCTestsArray(allTestsInC));
|
generateFile(__dirname + '/java/tests/com/facebook/csslayout/LayoutEngineTest.java', JavaTranspiler.transpileCTestsArray(allTestsInC));
|
||||||
|
generateFile(__dirname + '/csharp/Facebook.CSSLayout/LayoutEngine.cs', CSharpTranspiler.transpileLayoutEngine(computeLayout.toString()));
|
||||||
|
generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/TestConstants.cs', CSharpTranspiler.transpileCConstDefs(makeConstDefs()));
|
||||||
|
generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs', CSharpTranspiler.transpileCTestsArray(allTestsInC));
|
||||||
|
Reference in New Issue
Block a user