2015-02-20 23:45:12 +00:00
/**
2016-06-15 16:44:30 +00:00
* Copyright ( c ) 2014 - present , Facebook , Inc .
2015-02-20 23:45:12 +00:00
* All rights reserved .
2016-07-13 17:15:44 +00:00
*
2015-02-20 23:45:12 +00:00
* 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 .
*/
2016-06-15 16:44:30 +00:00
# include <assert.h>
# include <math.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2016-07-20 15:46:00 +00:00
# include "CSSLayout-internal.h"
2016-06-15 16:44:30 +00:00
# ifdef _MSC_VER
# include <float.h>
# define isnan _isnan
/* define fmaxf if < VC12 */
# if _MSC_VER < 1800
__forceinline const float fmaxf ( const float a , const float b ) {
return ( a > b ) ? a : b ;
}
# endif
# endif
# define POSITIVE_FLEX_IS_AUTO 0
2016-07-20 15:46:00 +00:00
CSSNodeRef CSSNodeNew ( ) {
2016-07-20 20:26:57 +00:00
CSSNodeRef node = calloc ( 1 , sizeof ( CSSNode ) ) ;
assert ( node ! = NULL ) ;
2016-07-20 15:46:00 +00:00
CSSNodeInit ( node ) ;
return node ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 15:46:00 +00:00
void CSSNodeFree ( CSSNodeRef node ) {
2016-07-20 20:26:57 +00:00
CSSNodeListFree ( node - > children ) ;
2016-07-20 15:46:00 +00:00
free ( node ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 15:46:00 +00:00
void CSSNodeInit ( CSSNodeRef node ) {
2016-07-22 18:36:15 +00:00
node - > parent = NULL ;
node - > children = CSSNodeListNew ( 4 ) ;
node - > shouldUpdate = true ;
node - > isDirty = false ;
2016-07-20 13:40:26 +00:00
node - > style . alignItems = CSSAlignStretch ;
node - > style . alignContent = CSSAlignFlexStart ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > style . direction = CSSDirectionInherit ;
node - > style . flexDirection = CSSFlexDirectionColumn ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > style . overflow = CSSOverflowVisible ;
2016-06-15 16:44:30 +00:00
// Some of the fields default to undefined and not 0
2016-07-20 13:40:26 +00:00
node - > style . dimensions [ CSSDimensionWidth ] = CSSUndefined ;
node - > style . dimensions [ CSSDimensionHeight ] = CSSUndefined ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > style . minDimensions [ CSSDimensionWidth ] = CSSUndefined ;
node - > style . minDimensions [ CSSDimensionHeight ] = CSSUndefined ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > style . maxDimensions [ CSSDimensionWidth ] = CSSUndefined ;
node - > style . maxDimensions [ CSSDimensionHeight ] = CSSUndefined ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > style . position [ CSSPositionLeft ] = CSSUndefined ;
node - > style . position [ CSSPositionTop ] = CSSUndefined ;
node - > style . position [ CSSPositionRight ] = CSSUndefined ;
node - > style . position [ CSSPositionBottom ] = CSSUndefined ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > style . margin [ CSSPositionStart ] = CSSUndefined ;
node - > style . margin [ CSSPositionEnd ] = CSSUndefined ;
node - > style . padding [ CSSPositionStart ] = CSSUndefined ;
node - > style . padding [ CSSPositionEnd ] = CSSUndefined ;
node - > style . border [ CSSPositionStart ] = CSSUndefined ;
node - > style . border [ CSSPositionEnd ] = CSSUndefined ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
node - > layout . dimensions [ CSSDimensionWidth ] = CSSUndefined ;
node - > layout . dimensions [ CSSDimensionHeight ] = CSSUndefined ;
2016-06-15 16:44:30 +00:00
// Such that the comparison is always going to be false
2016-07-20 13:40:26 +00:00
node - > layout . lastParentDirection = ( CSSDirection ) - 1 ;
node - > layout . nextCachedMeasurementsIndex = 0 ;
node - > layout . measuredDimensions [ CSSDimensionWidth ] = CSSUndefined ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = CSSUndefined ;
node - > layout . cached_layout . widthMeasureMode = ( CSSMeasureMode ) - 1 ;
node - > layout . cached_layout . heightMeasureMode = ( CSSMeasureMode ) - 1 ;
2016-06-15 16:44:30 +00:00
}
2016-07-22 18:36:15 +00:00
void _CSSNodeMarkDirty ( CSSNodeRef node ) {
if ( ! node - > isDirty ) {
node - > isDirty = true ;
if ( node - > parent ) {
_CSSNodeMarkDirty ( node - > parent ) ;
}
}
}
2016-07-20 20:26:57 +00:00
void CSSNodeInsertChild ( CSSNodeRef node , CSSNodeRef child , unsigned int index ) {
CSSNodeListInsert ( node - > children , child , index ) ;
2016-07-22 18:36:15 +00:00
child - > parent = node ;
_CSSNodeMarkDirty ( node ) ;
2016-07-20 20:26:57 +00:00
}
void CSSNodeRemoveChild ( CSSNodeRef node , CSSNodeRef child ) {
CSSNodeListDelete ( node - > children , child ) ;
2016-07-22 18:36:15 +00:00
child - > parent = NULL ;
_CSSNodeMarkDirty ( node ) ;
2016-07-20 20:26:57 +00:00
}
CSSNodeRef CSSNodeGetChild ( CSSNodeRef node , unsigned int index ) {
return CSSNodeListGet ( node - > children , index ) ;
}
unsigned int CSSNodeChildCount ( CSSNodeRef node ) {
return CSSNodeListCount ( node - > children ) ;
}
2016-07-22 18:36:15 +00:00
void CSSNodeMarkDirty ( CSSNodeRef node ) {
// Nodes without custom measure functions should not manually mark themselves as dirty
assert ( node - > measure ! = NULL ) ;
_CSSNodeMarkDirty ( node ) ;
}
2016-07-20 15:46:00 +00:00
# define CSS_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \
void CSSNodeSet # # name ( CSSNodeRef node , type paramName ) { \
node - > instanceName = paramName ; \
} \
\
type CSSNodeGet # # name ( CSSNodeRef node ) { \
return node - > instanceName ; \
} \
# define CSS_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \
void CSSNodeStyleSet # # name ( CSSNodeRef node , type paramName ) { \
node - > style . instanceName = paramName ; \
2016-07-22 18:36:15 +00:00
_CSSNodeMarkDirty ( node ) ; \
2016-07-20 15:46:00 +00:00
} \
\
type CSSNodeStyleGet # # name ( CSSNodeRef node ) { \
return node - > style . instanceName ; \
} \
# define CSS_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \
type CSSNodeLayoutGet # # name ( CSSNodeRef node ) { \
return node - > layout . instanceName ; \
} \
CSS_NODE_PROPERTY_IMPL ( void * , Context , context , context ) ;
CSS_NODE_PROPERTY_IMPL ( CSSMeasureFunc , MeasureFunc , measureFunc , measure ) ;
CSS_NODE_PROPERTY_IMPL ( CSSPrintFunc , PrintFunc , printFunc , print ) ;
2016-07-20 15:46:04 +00:00
CSS_NODE_PROPERTY_IMPL ( bool , IsTextnode , isTextNode , isTextNode ) ;
2016-07-20 15:46:00 +00:00
CSS_NODE_PROPERTY_IMPL ( bool , ShouldUpdate , shouldUpdate , shouldUpdate ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSDirection , Direction , direction , direction ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSFlexDirection , FlexDirection , flexDirection , flexDirection ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSJustify , JustifyContent , justifyContent , justifyContent ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSAlign , AlignContent , alignContent , alignContent ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSAlign , AlignItems , alignItems , alignItems ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSAlign , AlignSelf , alignSelf , alignSelf ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSPositionType , PositionType , positionType , positionType ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSWrapType , FlexWrap , flexWrap , flexWrap ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( CSSOverflow , Overflow , overflow , overflow ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , Flex , flex , flex ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PositionLeft , positionLeft , position [ CSSPositionLeft ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PositionTop , positionTop , position [ CSSPositionTop ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PositionRight , positionRight , position [ CSSPositionRight ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PositionBottom , positionBottom , position [ CSSPositionBottom ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MarginLeft , marginLeft , margin [ CSSPositionLeft ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MarginTop , marginTop , margin [ CSSPositionTop ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MarginRight , marginRight , margin [ CSSPositionRight ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MarginBottom , marginBottom , margin [ CSSPositionBottom ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MarginStart , marginStart , margin [ CSSPositionStart ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MarginEnd , marginEnd , margin [ CSSPositionEnd ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PaddingLeft , paddingLeft , padding [ CSSPositionLeft ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PaddingTop , paddingTop , padding [ CSSPositionTop ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PaddingRight , paddingRight , padding [ CSSPositionRight ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PaddingBottom , paddingBottom , padding [ CSSPositionBottom ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PaddingStart , paddingStart , padding [ CSSPositionStart ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , PaddingEnd , paddingEnd , padding [ CSSPositionEnd ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , BorderLeft , borderLeft , border [ CSSPositionLeft ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , BorderTop , borderTop , border [ CSSPositionTop ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , BorderRight , borderRight , border [ CSSPositionRight ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , BorderBottom , borderBottom , border [ CSSPositionBottom ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , BorderStart , borderStart , border [ CSSPositionStart ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , BorderEnd , BorderEnd , border [ CSSPositionEnd ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , Width , width , dimensions [ CSSDimensionWidth ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , Height , height , dimensions [ CSSDimensionHeight ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MinWidth , minWidth , minDimensions [ CSSDimensionWidth ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MinHeight , minHeight , minDimensions [ CSSDimensionHeight ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MaxWidth , maxWidth , maxDimensions [ CSSDimensionWidth ] ) ;
CSS_NODE_STYLE_PROPERTY_IMPL ( float , MaxHeight , maxHeight , maxDimensions [ CSSDimensionHeight ] ) ;
CSS_NODE_LAYOUT_PROPERTY_IMPL ( float , Left , position [ CSSPositionLeft ] ) ;
CSS_NODE_LAYOUT_PROPERTY_IMPL ( float , Top , position [ CSSPositionTop ] ) ;
CSS_NODE_LAYOUT_PROPERTY_IMPL ( float , Right , position [ CSSPositionRight ] ) ;
CSS_NODE_LAYOUT_PROPERTY_IMPL ( float , Bottom , position [ CSSPositionBottom ] ) ;
CSS_NODE_LAYOUT_PROPERTY_IMPL ( float , Width , dimensions [ CSSDimensionWidth ] ) ;
CSS_NODE_LAYOUT_PROPERTY_IMPL ( float , Height , dimensions [ CSSDimensionHeight ] ) ;
int gCurrentGenerationCount = 0 ;
bool layoutNodeInternal ( CSSNode * node , float availableWidth , float availableHeight , CSSDirection parentDirection ,
CSSMeasureMode widthMeasureMode , CSSMeasureMode heightMeasureMode , bool performLayout , char * reason ) ;
bool isUndefined ( float value ) {
return isnan ( value ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 15:46:00 +00:00
static bool eq ( float a , float b ) {
if ( isUndefined ( a ) ) {
return isUndefined ( b ) ;
}
return fabs ( a - b ) < 0.0001 ;
2016-06-15 16:44:30 +00:00
}
static void indent ( int n ) {
for ( int i = 0 ; i < n ; + + i ) {
printf ( " " ) ;
}
}
static void print_number_0 ( const char * str , float number ) {
if ( ! eq ( number , 0 ) ) {
printf ( " %s: %g, " , str , number ) ;
}
}
static void print_number_nan ( const char * str , float number ) {
if ( ! isnan ( number ) ) {
printf ( " %s: %g, " , str , number ) ;
}
}
static bool four_equal ( float four [ 4 ] ) {
return
eq ( four [ 0 ] , four [ 1 ] ) & &
eq ( four [ 0 ] , four [ 2 ] ) & &
eq ( four [ 0 ] , four [ 3 ] ) ;
}
static void print_css_node_rec (
2016-07-20 13:40:26 +00:00
CSSNode * node ,
CSSPrintOptions options ,
2016-06-15 16:44:30 +00:00
int level
) {
indent ( level ) ;
printf ( " { " ) ;
if ( node - > print ) {
node - > print ( node - > context ) ;
}
2016-07-20 13:40:26 +00:00
if ( options & CSSPrintOptionsLayout ) {
2016-06-15 16:44:30 +00:00
printf ( " layout: { " ) ;
2016-07-20 13:40:26 +00:00
printf ( " width: %g, " , node - > layout . dimensions [ CSSDimensionWidth ] ) ;
printf ( " height: %g, " , node - > layout . dimensions [ CSSDimensionHeight ] ) ;
printf ( " top: %g, " , node - > layout . position [ CSSPositionTop ] ) ;
printf ( " left: %g " , node - > layout . position [ CSSPositionLeft ] ) ;
2016-06-15 16:44:30 +00:00
printf ( " }, " ) ;
}
2016-07-20 13:40:26 +00:00
if ( options & CSSPrintOptionsStyle ) {
if ( node - > style . flexDirection = = CSSFlexDirectionColumn ) {
2016-06-15 16:44:30 +00:00
printf ( " flexDirection: 'column', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . flexDirection = = CSSFlexDirectionColumnReverse ) {
2016-06-15 16:44:30 +00:00
printf ( " flexDirection: 'column-reverse', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . flexDirection = = CSSFlexDirectionRow ) {
2016-06-15 16:44:30 +00:00
printf ( " flexDirection: 'row', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . flexDirection = = CSSFlexDirectionRowReverse ) {
2016-06-15 16:44:30 +00:00
printf ( " flexDirection: 'row-reverse', " ) ;
}
2016-07-20 13:40:26 +00:00
if ( node - > style . justifyContent = = CSSJustifyCenter ) {
2016-06-15 16:44:30 +00:00
printf ( " justifyContent: 'center', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . justifyContent = = CSSJustifyFlexEnd ) {
2016-06-15 16:44:30 +00:00
printf ( " justifyContent: 'flex-end', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . justifyContent = = CSSJustifySpaceAround ) {
2016-06-15 16:44:30 +00:00
printf ( " justifyContent: 'space-around', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . justifyContent = = CSSJustifySpaceBetween ) {
2016-06-15 16:44:30 +00:00
printf ( " justifyContent: 'space-between', " ) ;
}
2016-07-20 13:40:26 +00:00
if ( node - > style . alignItems = = CSSAlignCenter ) {
2016-06-15 16:44:30 +00:00
printf ( " alignItems: 'center', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignItems = = CSSAlignFlexEnd ) {
2016-06-15 16:44:30 +00:00
printf ( " alignItems: 'flex-end', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignItems = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
printf ( " alignItems: 'stretch', " ) ;
}
2016-07-20 13:40:26 +00:00
if ( node - > style . alignContent = = CSSAlignCenter ) {
2016-06-15 16:44:30 +00:00
printf ( " alignContent: 'center', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignContent = = CSSAlignFlexEnd ) {
2016-06-15 16:44:30 +00:00
printf ( " alignContent: 'flex-end', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignContent = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
printf ( " alignContent: 'stretch', " ) ;
}
2016-07-20 13:40:26 +00:00
if ( node - > style . alignSelf = = CSSAlignFlexStart ) {
2016-06-15 16:44:30 +00:00
printf ( " alignSelf: 'flex-start', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignSelf = = CSSAlignCenter ) {
2016-06-15 16:44:30 +00:00
printf ( " alignSelf: 'center', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignSelf = = CSSAlignFlexEnd ) {
2016-06-15 16:44:30 +00:00
printf ( " alignSelf: 'flex-end', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . alignSelf = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
printf ( " alignSelf: 'stretch', " ) ;
}
print_number_nan ( " flex " , node - > style . flex ) ;
2016-07-20 13:40:26 +00:00
if ( node - > style . overflow = = CSSOverflowHidden ) {
2016-06-15 16:44:30 +00:00
printf ( " overflow: 'hidden', " ) ;
2016-07-20 13:40:26 +00:00
} else if ( node - > style . overflow = = CSSOverflowVisible ) {
2016-06-15 16:44:30 +00:00
printf ( " overflow: 'visible', " ) ;
}
if ( four_equal ( node - > style . margin ) ) {
2016-07-20 13:40:26 +00:00
print_number_0 ( " margin " , node - > style . margin [ CSSPositionLeft ] ) ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
print_number_0 ( " marginLeft " , node - > style . margin [ CSSPositionLeft ] ) ;
print_number_0 ( " marginRight " , node - > style . margin [ CSSPositionRight ] ) ;
print_number_0 ( " marginTop " , node - > style . margin [ CSSPositionTop ] ) ;
print_number_0 ( " marginBottom " , node - > style . margin [ CSSPositionBottom ] ) ;
print_number_0 ( " marginStart " , node - > style . margin [ CSSPositionStart ] ) ;
print_number_0 ( " marginEnd " , node - > style . margin [ CSSPositionEnd ] ) ;
2016-06-15 16:44:30 +00:00
}
if ( four_equal ( node - > style . padding ) ) {
2016-07-20 13:40:26 +00:00
print_number_0 ( " padding " , node - > style . padding [ CSSPositionLeft ] ) ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
print_number_0 ( " paddingLeft " , node - > style . padding [ CSSPositionLeft ] ) ;
print_number_0 ( " paddingRight " , node - > style . padding [ CSSPositionRight ] ) ;
print_number_0 ( " paddingTop " , node - > style . padding [ CSSPositionTop ] ) ;
print_number_0 ( " paddingBottom " , node - > style . padding [ CSSPositionBottom ] ) ;
print_number_0 ( " paddingStart " , node - > style . padding [ CSSPositionStart ] ) ;
print_number_0 ( " paddingEnd " , node - > style . padding [ CSSPositionEnd ] ) ;
2016-06-15 16:44:30 +00:00
}
if ( four_equal ( node - > style . border ) ) {
2016-07-20 13:40:26 +00:00
print_number_0 ( " borderWidth " , node - > style . border [ CSSPositionLeft ] ) ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
print_number_0 ( " borderLeftWidth " , node - > style . border [ CSSPositionLeft ] ) ;
print_number_0 ( " borderRightWidth " , node - > style . border [ CSSPositionRight ] ) ;
print_number_0 ( " borderTopWidth " , node - > style . border [ CSSPositionTop ] ) ;
print_number_0 ( " borderBottomWidth " , node - > style . border [ CSSPositionBottom ] ) ;
print_number_0 ( " borderStartWidth " , node - > style . border [ CSSPositionStart ] ) ;
print_number_0 ( " borderEndWidth " , node - > style . border [ CSSPositionEnd ] ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
print_number_nan ( " width " , node - > style . dimensions [ CSSDimensionWidth ] ) ;
print_number_nan ( " height " , node - > style . dimensions [ CSSDimensionHeight ] ) ;
print_number_nan ( " maxWidth " , node - > style . maxDimensions [ CSSDimensionWidth ] ) ;
print_number_nan ( " maxHeight " , node - > style . maxDimensions [ CSSDimensionHeight ] ) ;
print_number_nan ( " minWidth " , node - > style . minDimensions [ CSSDimensionWidth ] ) ;
print_number_nan ( " minHeight " , node - > style . minDimensions [ CSSDimensionHeight ] ) ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( node - > style . positionType = = CSSPositionTypeAbsolute ) {
2016-06-15 16:44:30 +00:00
printf ( " position: 'absolute', " ) ;
}
2016-07-20 13:40:26 +00:00
print_number_nan ( " left " , node - > style . position [ CSSPositionLeft ] ) ;
print_number_nan ( " right " , node - > style . position [ CSSPositionRight ] ) ;
print_number_nan ( " top " , node - > style . position [ CSSPositionTop ] ) ;
print_number_nan ( " bottom " , node - > style . position [ CSSPositionBottom ] ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 20:26:57 +00:00
unsigned int childCount = CSSNodeListCount ( node - > children ) ;
if ( options & CSSPrintOptionsChildren & & childCount > 0 ) {
2016-06-15 16:44:30 +00:00
printf ( " children: [ \n " ) ;
2016-07-20 20:26:57 +00:00
for ( unsigned int i = 0 ; i < childCount ; + + i ) {
print_css_node_rec ( CSSNodeGetChild ( node , i ) , options , level + 1 ) ;
2016-06-15 16:44:30 +00:00
}
indent ( level ) ;
printf ( " ]}, \n " ) ;
} else {
printf ( " }, \n " ) ;
}
}
2016-07-20 13:40:26 +00:00
void CSSNodePrint ( CSSNode * node , CSSPrintOptions options ) {
2016-06-15 16:44:30 +00:00
print_css_node_rec ( node , options , 0 ) ;
}
2016-07-20 13:40:26 +00:00
static CSSPosition leading [ 4 ] = {
/* CSSFlexDirectionColumn = */ CSSPositionTop ,
/* CSSFlexDirectionColumnReverse = */ CSSPositionBottom ,
/* CSSFlexDirectionRow = */ CSSPositionLeft ,
/* CSSFlexDirectionRowReverse = */ CSSPositionRight
2016-06-15 16:44:30 +00:00
} ;
2016-07-20 13:40:26 +00:00
static CSSPosition trailing [ 4 ] = {
/* CSSFlexDirectionColumn = */ CSSPositionBottom ,
/* CSSFlexDirectionColumnReverse = */ CSSPositionTop ,
/* CSSFlexDirectionRow = */ CSSPositionRight ,
/* CSSFlexDirectionRowReverse = */ CSSPositionLeft
2016-06-15 16:44:30 +00:00
} ;
2016-07-20 13:40:26 +00:00
static CSSPosition pos [ 4 ] = {
/* CSSFlexDirectionColumn = */ CSSPositionTop ,
/* CSSFlexDirectionColumnReverse = */ CSSPositionBottom ,
/* CSSFlexDirectionRow = */ CSSPositionLeft ,
/* CSSFlexDirectionRowReverse = */ CSSPositionRight
2016-06-15 16:44:30 +00:00
} ;
2016-07-20 13:40:26 +00:00
static CSSDimension dim [ 4 ] = {
/* CSSFlexDirectionColumn = */ CSSDimensionHeight ,
/* CSSFlexDirectionColumnReverse = */ CSSDimensionHeight ,
/* CSSFlexDirectionRow = */ CSSDimensionWidth ,
/* CSSFlexDirectionRowReverse = */ CSSDimensionWidth
2016-06-15 16:44:30 +00:00
} ;
2016-07-20 13:40:26 +00:00
static bool isRowDirection ( CSSFlexDirection flexDirection ) {
return flexDirection = = CSSFlexDirectionRow | |
flexDirection = = CSSFlexDirectionRowReverse ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
static bool isColumnDirection ( CSSFlexDirection flexDirection ) {
return flexDirection = = CSSFlexDirectionColumn | |
flexDirection = = CSSFlexDirectionColumnReverse ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
static bool isFlexBasisAuto ( CSSNode * node ) {
2016-06-15 16:44:30 +00:00
# if POSITIVE_FLEX_IS_AUTO
// All flex values are auto.
( void ) node ;
return true ;
# else
// A flex value > 0 implies a basis of zero.
return node - > style . flex < = 0 ;
# endif
}
2016-07-20 13:40:26 +00:00
static float getFlexGrowFactor ( CSSNode * node ) {
2016-06-15 16:44:30 +00:00
// Flex grow is implied by positive values for flex.
if ( node - > style . flex > 0 ) {
return node - > style . flex ;
}
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float getFlexShrinkFactor ( CSSNode * node ) {
2016-06-15 16:44:30 +00:00
# if POSITIVE_FLEX_IS_AUTO
// A flex shrink factor of 1 is implied by non-zero values for flex.
if ( node - > style . flex ! = 0 ) {
return 1 ;
}
# else
// A flex shrink factor of 1 is implied by negative values for flex.
if ( node - > style . flex < 0 ) {
return 1 ;
}
# endif
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float getLeadingMargin ( CSSNode * node , CSSFlexDirection axis ) {
if ( isRowDirection ( axis ) & & ! isUndefined ( node - > style . margin [ CSSPositionStart ] ) ) {
return node - > style . margin [ CSSPositionStart ] ;
2016-06-15 16:44:30 +00:00
}
return node - > style . margin [ leading [ axis ] ] ;
}
2016-07-20 13:40:26 +00:00
static float getTrailingMargin ( CSSNode * node , CSSFlexDirection axis ) {
if ( isRowDirection ( axis ) & & ! isUndefined ( node - > style . margin [ CSSPositionEnd ] ) ) {
return node - > style . margin [ CSSPositionEnd ] ;
2016-06-15 16:44:30 +00:00
}
return node - > style . margin [ trailing [ axis ] ] ;
}
2016-07-20 13:40:26 +00:00
static float getLeadingPadding ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
if ( isRowDirection ( axis ) & &
2016-07-20 13:40:26 +00:00
! isUndefined ( node - > style . padding [ CSSPositionStart ] ) & &
node - > style . padding [ CSSPositionStart ] > = 0 ) {
return node - > style . padding [ CSSPositionStart ] ;
2016-06-15 16:44:30 +00:00
}
if ( node - > style . padding [ leading [ axis ] ] > = 0 ) {
return node - > style . padding [ leading [ axis ] ] ;
}
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float getTrailingPadding ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
if ( isRowDirection ( axis ) & &
2016-07-20 13:40:26 +00:00
! isUndefined ( node - > style . padding [ CSSPositionEnd ] ) & &
node - > style . padding [ CSSPositionEnd ] > = 0 ) {
return node - > style . padding [ CSSPositionEnd ] ;
2016-06-15 16:44:30 +00:00
}
if ( node - > style . padding [ trailing [ axis ] ] > = 0 ) {
return node - > style . padding [ trailing [ axis ] ] ;
}
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float getLeadingBorder ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
if ( isRowDirection ( axis ) & &
2016-07-20 13:40:26 +00:00
! isUndefined ( node - > style . border [ CSSPositionStart ] ) & &
node - > style . border [ CSSPositionStart ] > = 0 ) {
return node - > style . border [ CSSPositionStart ] ;
2016-06-15 16:44:30 +00:00
}
if ( node - > style . border [ leading [ axis ] ] > = 0 ) {
return node - > style . border [ leading [ axis ] ] ;
}
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float getTrailingBorder ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
if ( isRowDirection ( axis ) & &
2016-07-20 13:40:26 +00:00
! isUndefined ( node - > style . border [ CSSPositionEnd ] ) & &
node - > style . border [ CSSPositionEnd ] > = 0 ) {
return node - > style . border [ CSSPositionEnd ] ;
2016-06-15 16:44:30 +00:00
}
if ( node - > style . border [ trailing [ axis ] ] > = 0 ) {
return node - > style . border [ trailing [ axis ] ] ;
}
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float getLeadingPaddingAndBorder ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
return getLeadingPadding ( node , axis ) + getLeadingBorder ( node , axis ) ;
}
2016-07-20 13:40:26 +00:00
static float getTrailingPaddingAndBorder ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
return getTrailingPadding ( node , axis ) + getTrailingBorder ( node , axis ) ;
}
2016-07-20 13:40:26 +00:00
static float getMarginAxis ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
return getLeadingMargin ( node , axis ) + getTrailingMargin ( node , axis ) ;
}
2016-07-20 13:40:26 +00:00
static float getPaddingAndBorderAxis ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
return getLeadingPaddingAndBorder ( node , axis ) + getTrailingPaddingAndBorder ( node , axis ) ;
}
2016-07-20 13:40:26 +00:00
static CSSAlign getAlignItem ( CSSNode * node , CSSNode * child ) {
if ( child - > style . alignSelf ! = CSSAlignAuto ) {
return child - > style . alignSelf ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
return node - > style . alignItems ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
static CSSDirection resolveDirection ( CSSNode * node , CSSDirection parentDirection ) {
CSSDirection direction = node - > style . direction ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( direction = = CSSDirectionInherit ) {
direction = parentDirection > CSSDirectionInherit ? parentDirection : CSSDirectionLTR ;
2016-06-15 16:44:30 +00:00
}
return direction ;
}
2016-07-20 13:40:26 +00:00
static CSSFlexDirection getFlexDirection ( CSSNode * node ) {
return node - > style . flexDirection ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
static CSSFlexDirection resolveAxis ( CSSFlexDirection flexDirection , CSSDirection direction ) {
if ( direction = = CSSDirectionRTL ) {
if ( flexDirection = = CSSFlexDirectionRow ) {
return CSSFlexDirectionRowReverse ;
} else if ( flexDirection = = CSSFlexDirectionRowReverse ) {
return CSSFlexDirectionRow ;
2016-06-15 16:44:30 +00:00
}
}
2016-07-20 13:40:26 +00:00
return flexDirection ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
static CSSFlexDirection getCrossFlexDirection ( CSSFlexDirection flexDirection , CSSDirection direction ) {
if ( isColumnDirection ( flexDirection ) ) {
return resolveAxis ( CSSFlexDirectionRow , direction ) ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
return CSSFlexDirectionColumn ;
2016-06-15 16:44:30 +00:00
}
}
2016-07-20 13:40:26 +00:00
static float getFlex ( CSSNode * node ) {
2016-06-15 16:44:30 +00:00
return node - > style . flex ;
}
2016-07-20 13:40:26 +00:00
static bool isFlex ( CSSNode * node ) {
2016-06-15 16:44:30 +00:00
return (
2016-07-20 13:40:26 +00:00
node - > style . positionType = = CSSPositionTypeRelative & &
2016-06-15 16:44:30 +00:00
getFlex ( node ) ! = 0
) ;
}
2016-07-20 13:40:26 +00:00
static bool isFlexWrap ( CSSNode * node ) {
return node - > style . flexWrap = = CSSWrapTypeWrap ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
static float getDimWithMargin ( CSSNode * node , CSSFlexDirection axis ) {
return node - > layout . measuredDimensions [ dim [ axis ] ] +
2016-06-15 16:44:30 +00:00
getLeadingMargin ( node , axis ) +
getTrailingMargin ( node , axis ) ;
}
2016-07-20 13:40:26 +00:00
static bool isStyleDimDefined ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
float value = node - > style . dimensions [ dim [ axis ] ] ;
return ! isUndefined ( value ) & & value > = 0.0 ;
}
2016-07-20 13:40:26 +00:00
static bool isLayoutDimDefined ( CSSNode * node , CSSFlexDirection axis ) {
float value = node - > layout . measuredDimensions [ dim [ axis ] ] ;
2016-06-15 16:44:30 +00:00
return ! isUndefined ( value ) & & value > = 0.0 ;
}
2016-07-20 13:40:26 +00:00
static bool isPosDefined ( CSSNode * node , CSSPosition position ) {
2016-06-15 16:44:30 +00:00
return ! isUndefined ( node - > style . position [ position ] ) ;
}
2016-07-20 13:40:26 +00:00
static bool isMeasureDefined ( CSSNode * node ) {
2016-06-15 16:44:30 +00:00
return node - > measure ;
}
2016-07-20 13:40:26 +00:00
static float getPosition ( CSSNode * node , CSSPosition position ) {
2016-06-15 16:44:30 +00:00
float result = node - > style . position [ position ] ;
if ( ! isUndefined ( result ) ) {
return result ;
}
return 0 ;
}
2016-07-20 13:40:26 +00:00
static float boundAxisWithinMinAndMax ( CSSNode * node , CSSFlexDirection axis , float value ) {
float min = CSSUndefined ;
float max = CSSUndefined ;
2016-06-15 16:44:30 +00:00
if ( isColumnDirection ( axis ) ) {
2016-07-20 13:40:26 +00:00
min = node - > style . minDimensions [ CSSDimensionHeight ] ;
max = node - > style . maxDimensions [ CSSDimensionHeight ] ;
2016-06-15 16:44:30 +00:00
} else if ( isRowDirection ( axis ) ) {
2016-07-20 13:40:26 +00:00
min = node - > style . minDimensions [ CSSDimensionWidth ] ;
max = node - > style . maxDimensions [ CSSDimensionWidth ] ;
2016-06-15 16:44:30 +00:00
}
float boundValue = value ;
if ( ! isUndefined ( max ) & & max > = 0.0 & & boundValue > max ) {
boundValue = max ;
}
if ( ! isUndefined ( min ) & & min > = 0.0 & & boundValue < min ) {
boundValue = min ;
}
return boundValue ;
}
// Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the
// padding and border amount.
2016-07-20 13:40:26 +00:00
static float boundAxis ( CSSNode * node , CSSFlexDirection axis , float value ) {
2016-06-15 16:44:30 +00:00
return fmaxf ( boundAxisWithinMinAndMax ( node , axis , value ) , getPaddingAndBorderAxis ( node , axis ) ) ;
}
2016-07-20 13:40:26 +00:00
static void setTrailingPosition ( CSSNode * node , CSSNode * child , CSSFlexDirection axis ) {
float size = child - > style . positionType = = CSSPositionTypeAbsolute ?
2016-06-15 16:44:30 +00:00
0 :
2016-07-20 13:40:26 +00:00
child - > layout . measuredDimensions [ dim [ axis ] ] ;
child - > layout . position [ trailing [ axis ] ] = node - > layout . measuredDimensions [ dim [ axis ] ] - size - child - > layout . position [ pos [ axis ] ] ;
2016-06-15 16:44:30 +00:00
}
// If both left and right are defined, then use left. Otherwise return
// +left or -right depending on which is defined.
2016-07-20 13:40:26 +00:00
static float getRelativePosition ( CSSNode * node , CSSFlexDirection axis ) {
2016-06-15 16:44:30 +00:00
float lead = node - > style . position [ leading [ axis ] ] ;
if ( ! isUndefined ( lead ) ) {
return lead ;
}
return - getPosition ( node , trailing [ axis ] ) ;
}
2016-07-20 13:40:26 +00:00
static void setPosition ( CSSNode * node , CSSDirection direction ) {
CSSFlexDirection mainAxis = resolveAxis ( getFlexDirection ( node ) , direction ) ;
CSSFlexDirection crossAxis = getCrossFlexDirection ( mainAxis , direction ) ;
2016-06-15 16:44:30 +00:00
node - > layout . position [ leading [ mainAxis ] ] = getLeadingMargin ( node , mainAxis ) +
getRelativePosition ( node , mainAxis ) ;
node - > layout . position [ trailing [ mainAxis ] ] = getTrailingMargin ( node , mainAxis ) +
getRelativePosition ( node , mainAxis ) ;
node - > layout . position [ leading [ crossAxis ] ] = getLeadingMargin ( node , crossAxis ) +
getRelativePosition ( node , crossAxis ) ;
node - > layout . position [ trailing [ crossAxis ] ] = getTrailingMargin ( node , crossAxis ) +
getRelativePosition ( node , crossAxis ) ;
}
//
// This is the main routine that implements a subset of the flexbox layout algorithm
// described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.
//
// Limitations of this algorithm, compared to the full standard:
// * Display property is always assumed to be 'flex' except for Text nodes, which
// are assumed to be 'inline-flex'.
// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are
// stacked in document order.
// * The 'order' property is not supported. The order of flex items is always defined
// by document order.
// * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'
// and 'hidden' are not supported.
// * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The
// rarely-used 'wrap-reverse' is not supported.
// * Rather than allowing arbitrary combinations of flexGrow, flexShrink and
// flexBasis, this algorithm supports only the three most common combinations:
// flex: 0 is equiavlent to flex: 0 0 auto
// flex: n (where n is a positive value) is equivalent to flex: n 1 auto
// If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0
// This is faster because the content doesn't need to be measured, but it's
// less flexible because the basis is always 0 and can't be overriden with
// the width/height attributes.
// flex: -1 (or any negative value) is equivalent to flex: 0 1 auto
// * Margins cannot be specified as 'auto'. They must be specified in terms of pixel
// values, and the default value is 0.
// * The 'baseline' value is not supported for alignItems and alignSelf properties.
// * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be
// specified as pixel values, not as percentages.
// * There is no support for calculation of dimensions based on intrinsic aspect ratios
// (e.g. images).
// * There is no support for forced breaks.
// * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).
//
// Deviations from standard:
// * Section 4.5 of the spec indicates that all flex items have a default minimum
// main size. For text blocks, for example, this is the width of the widest word.
// Calculating the minimum width is expensive, so we forego it and assume a default
// minimum main size of 0.
// * Min/Max sizes in the main axis are not honored when resolving flexible lengths.
// * The spec indicates that the default value for 'flexDirection' is 'row', but
// the algorithm below assumes a default of 'column'.
//
// Input parameters:
// - node: current node to be sized and layed out
// - availableWidth & availableHeight: available size to be used for sizing the node
2016-07-20 13:40:26 +00:00
// or CSSUndefined if the size is not available; interpretation depends on layout
2016-06-15 16:44:30 +00:00
// flags
// - parentDirection: the inline (text) direction within the parent (left-to-right or
// right-to-left)
// - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)
// - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)
// - performLayout: specifies whether the caller is interested in just the dimensions
// of the node or it requires the entire node and its subtree to be layed out
// (with final positions)
//
// Details:
// This routine is called recursively to lay out subtrees of flexbox elements. It uses the
// information in node.style, which is treated as a read-only input. It is responsible for
2016-07-20 13:40:26 +00:00
// setting the layout.direction and layout.measuredDimensions fields for the input node as well
// as the layout.position and layout.lineIndex fields for its child nodes. The
// layout.measuredDimensions field includes any border or padding for the node but does
2016-06-15 16:44:30 +00:00
// not include margins.
//
// The spec describes four different layout modes: "fill available", "max content", "min content",
// and "fit content". Of these, we don't use "min content" because we don't support default
// minimum main sizes (see above for details). Each of our measure modes maps to a layout mode
// from the spec (https://www.w3.org/TR/css3-sizing/#terms):
2016-07-20 13:40:26 +00:00
// - CSSMeasureModeUndefined: max content
// - CSSMeasureModeExactly: fill available
// - CSSMeasureModeAtMost: fit content
2016-06-15 16:44:30 +00:00
//
// When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of
2016-07-20 13:40:26 +00:00
// undefined then it must also pass a measure mode of CSSMeasureModeUndefined in that dimension.
2016-06-15 16:44:30 +00:00
//
2016-07-20 13:40:26 +00:00
static void layoutNodeImpl ( CSSNode * node , float availableWidth , float availableHeight ,
CSSDirection parentDirection , CSSMeasureMode widthMeasureMode , CSSMeasureMode heightMeasureMode , bool performLayout ) {
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
assert ( isUndefined ( availableWidth ) ? widthMeasureMode = = CSSMeasureModeUndefined : true ) ; // availableWidth is indefinite so widthMeasureMode must be CSSMeasureModeUndefined
assert ( isUndefined ( availableHeight ) ? heightMeasureMode = = CSSMeasureModeUndefined : true ) ; // availableHeight is indefinite so heightMeasureMode must be CSSMeasureModeUndefined
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
float paddingAndBorderAxisRow = getPaddingAndBorderAxis ( node , CSSFlexDirectionRow ) ;
float paddingAndBorderAxisColumn = getPaddingAndBorderAxis ( node , CSSFlexDirectionColumn ) ;
float marginAxisRow = getMarginAxis ( node , CSSFlexDirectionRow ) ;
float marginAxisColumn = getMarginAxis ( node , CSSFlexDirectionColumn ) ;
2016-06-15 16:44:30 +00:00
// Set the resolved resolution in the node's layout.
2016-07-20 13:40:26 +00:00
CSSDirection direction = resolveDirection ( node , parentDirection ) ;
2016-06-15 16:44:30 +00:00
node - > layout . direction = direction ;
// For content (text) nodes, determine the dimensions based on the text contents.
if ( isMeasureDefined ( node ) ) {
float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow ;
float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn ;
2016-07-20 13:40:26 +00:00
if ( widthMeasureMode = = CSSMeasureModeExactly & & heightMeasureMode = = CSSMeasureModeExactly ) {
2016-06-15 16:44:30 +00:00
// Don't bother sizing the text if both dimensions are already defined.
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , availableWidth - marginAxisRow ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , availableHeight - marginAxisColumn ) ;
2016-06-15 16:44:30 +00:00
} else if ( innerWidth < = 0 | | innerHeight < = 0 ) {
// Don't bother sizing the text if there's no horizontal or vertical space.
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , 0 ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , 0 ) ;
2016-06-15 16:44:30 +00:00
} else {
// Measure the text under the current constraints.
2016-07-20 15:46:00 +00:00
CSSSize measuredSize = node - > measure (
2016-06-15 16:44:30 +00:00
node - > context ,
2016-07-13 17:15:44 +00:00
2016-06-15 16:44:30 +00:00
innerWidth ,
widthMeasureMode ,
innerHeight ,
heightMeasureMode
) ;
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow ,
( widthMeasureMode = = CSSMeasureModeUndefined | | widthMeasureMode = = CSSMeasureModeAtMost ) ?
2016-07-20 15:46:00 +00:00
measuredSize . width + paddingAndBorderAxisRow :
2016-06-15 16:44:30 +00:00
availableWidth - marginAxisRow ) ;
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn ,
( heightMeasureMode = = CSSMeasureModeUndefined | | heightMeasureMode = = CSSMeasureModeAtMost ) ?
2016-07-20 15:46:00 +00:00
measuredSize . height + paddingAndBorderAxisColumn :
2016-06-15 16:44:30 +00:00
availableHeight - marginAxisColumn ) ;
}
return ;
}
// For nodes with no children, use the available values if they were provided, or
// the minimum size as indicated by the padding and border sizes.
2016-07-20 20:26:57 +00:00
unsigned int childCount = CSSNodeListCount ( node - > children ) ;
2016-06-15 16:44:30 +00:00
if ( childCount = = 0 ) {
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow ,
( widthMeasureMode = = CSSMeasureModeUndefined | | widthMeasureMode = = CSSMeasureModeAtMost ) ?
2016-06-15 16:44:30 +00:00
paddingAndBorderAxisRow :
availableWidth - marginAxisRow ) ;
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn ,
( heightMeasureMode = = CSSMeasureModeUndefined | | heightMeasureMode = = CSSMeasureModeAtMost ) ?
2016-06-15 16:44:30 +00:00
paddingAndBorderAxisColumn :
availableHeight - marginAxisColumn ) ;
return ;
}
// If we're not being asked to perform a full layout, we can handle a number of common
// cases here without incurring the cost of the remaining function.
if ( ! performLayout ) {
// If we're being asked to size the content with an at most constraint but there is no available width,
// the measurement will always be zero.
2016-07-20 13:40:26 +00:00
if ( widthMeasureMode = = CSSMeasureModeAtMost & & availableWidth < = 0 & &
heightMeasureMode = = CSSMeasureModeAtMost & & availableHeight < = 0 ) {
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , 0 ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , 0 ) ;
2016-06-15 16:44:30 +00:00
return ;
}
2016-07-20 13:40:26 +00:00
if ( widthMeasureMode = = CSSMeasureModeAtMost & & availableWidth < = 0 ) {
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , 0 ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , isUndefined ( availableHeight ) ? 0 : ( availableHeight - marginAxisColumn ) ) ;
2016-06-15 16:44:30 +00:00
return ;
}
2016-07-20 13:40:26 +00:00
if ( heightMeasureMode = = CSSMeasureModeAtMost & & availableHeight < = 0 ) {
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , isUndefined ( availableWidth ) ? 0 : ( availableWidth - marginAxisRow ) ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , 0 ) ;
2016-06-15 16:44:30 +00:00
return ;
}
// If we're being asked to use an exact width/height, there's no need to measure the children.
2016-07-20 13:40:26 +00:00
if ( widthMeasureMode = = CSSMeasureModeExactly & & heightMeasureMode = = CSSMeasureModeExactly ) {
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , availableWidth - marginAxisRow ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , availableHeight - marginAxisColumn ) ;
2016-06-15 16:44:30 +00:00
return ;
}
}
// STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
2016-07-20 13:40:26 +00:00
CSSFlexDirection mainAxis = resolveAxis ( getFlexDirection ( node ) , direction ) ;
CSSFlexDirection crossAxis = getCrossFlexDirection ( mainAxis , direction ) ;
2016-06-15 16:44:30 +00:00
bool isMainAxisRow = isRowDirection ( mainAxis ) ;
2016-07-20 13:40:26 +00:00
CSSJustify justifyContent = node - > style . justifyContent ;
2016-06-15 16:44:30 +00:00
bool isNodeFlexWrap = isFlexWrap ( node ) ;
2016-07-20 13:40:26 +00:00
CSSNode * firstAbsoluteChild = NULL ;
CSSNode * currentAbsoluteChild = NULL ;
2016-06-15 16:44:30 +00:00
float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder ( node , mainAxis ) ;
float trailingPaddingAndBorderMain = getTrailingPaddingAndBorder ( node , mainAxis ) ;
float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder ( node , crossAxis ) ;
float paddingAndBorderAxisMain = getPaddingAndBorderAxis ( node , mainAxis ) ;
float paddingAndBorderAxisCross = getPaddingAndBorderAxis ( node , crossAxis ) ;
2016-07-20 13:40:26 +00:00
CSSMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode ;
CSSMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode ;
2016-06-15 16:44:30 +00:00
// STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow ;
float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn ;
float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight ;
float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth ;
// STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
2016-07-20 13:40:26 +00:00
CSSNode * child ;
2016-07-20 20:26:57 +00:00
unsigned int i ;
2016-06-15 16:44:30 +00:00
float childWidth ;
float childHeight ;
2016-07-20 13:40:26 +00:00
CSSMeasureMode childWidthMeasureMode ;
CSSMeasureMode childHeightMeasureMode ;
2016-06-15 16:44:30 +00:00
for ( i = 0 ; i < childCount ; i + + ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , i ) ;
2016-06-15 16:44:30 +00:00
if ( performLayout ) {
// Set the initial position (relative to the parent).
2016-07-20 13:40:26 +00:00
CSSDirection childDirection = resolveDirection ( child , direction ) ;
2016-06-15 16:44:30 +00:00
setPosition ( child , childDirection ) ;
}
// Absolute-positioned children don't participate in flex layout. Add them
// to a list that we can process later.
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType = = CSSPositionTypeAbsolute ) {
2016-06-15 16:44:30 +00:00
// 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 ) {
2016-07-20 13:40:26 +00:00
currentAbsoluteChild - > nextChild = child ;
2016-06-15 16:44:30 +00:00
}
currentAbsoluteChild = child ;
2016-07-20 13:40:26 +00:00
child - > nextChild = NULL ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
if ( isMainAxisRow & & isStyleDimDefined ( child , CSSFlexDirectionRow ) ) {
2016-06-15 16:44:30 +00:00
// The width is definite, so use that as the flex basis.
2016-07-20 13:40:26 +00:00
child - > layout . flexBasis = fmaxf ( child - > style . dimensions [ CSSDimensionWidth ] , getPaddingAndBorderAxis ( child , CSSFlexDirectionRow ) ) ;
} else if ( ! isMainAxisRow & & isStyleDimDefined ( child , CSSFlexDirectionColumn ) ) {
2016-06-15 16:44:30 +00:00
// The height is definite, so use that as the flex basis.
2016-07-20 13:40:26 +00:00
child - > layout . flexBasis = fmaxf ( child - > style . dimensions [ CSSDimensionHeight ] , getPaddingAndBorderAxis ( child , CSSFlexDirectionColumn ) ) ;
2016-06-15 16:44:30 +00:00
} else if ( ! isFlexBasisAuto ( child ) & & ! isUndefined ( availableInnerMainDim ) ) {
// If the basis isn't 'auto', it is assumed to be zero.
2016-07-20 13:40:26 +00:00
child - > layout . flexBasis = fmaxf ( 0 , getPaddingAndBorderAxis ( child , mainAxis ) ) ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-08 21:47:31 +00:00
// Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).
2016-07-20 13:40:26 +00:00
childWidth = CSSUndefined ;
childHeight = CSSUndefined ;
childWidthMeasureMode = CSSMeasureModeUndefined ;
childHeightMeasureMode = CSSMeasureModeUndefined ;
if ( isStyleDimDefined ( child , CSSFlexDirectionRow ) ) {
childWidth = child - > style . dimensions [ CSSDimensionWidth ] + getMarginAxis ( child , CSSFlexDirectionRow ) ;
childWidthMeasureMode = CSSMeasureModeExactly ;
2016-07-08 21:47:31 +00:00
}
2016-07-20 13:40:26 +00:00
if ( isStyleDimDefined ( child , CSSFlexDirectionColumn ) ) {
childHeight = child - > style . dimensions [ CSSDimensionHeight ] + getMarginAxis ( child , CSSFlexDirectionColumn ) ;
childHeightMeasureMode = CSSMeasureModeExactly ;
2016-07-08 21:47:31 +00:00
}
// According to the spec, if the main size is not definite and the
// child's inline axis is parallel to the main axis (i.e. it's
// horizontal), the child should be sized using "UNDEFINED" in
// the main size. Otherwise use "AT_MOST" in the cross axis.
if ( ! isMainAxisRow & & isUndefined ( childWidth ) & & ! isUndefined ( availableInnerWidth ) ) {
childWidth = availableInnerWidth ;
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = CSSMeasureModeAtMost ;
2016-07-08 21:47:31 +00:00
}
// The W3C spec doesn't say anything about the 'overflow' property,
// but all major browsers appear to implement the following logic.
2016-07-20 13:40:26 +00:00
if ( node - > style . overflow = = CSSOverflowHidden ) {
2016-07-08 21:47:31 +00:00
if ( isMainAxisRow & & isUndefined ( childHeight ) & & ! isUndefined ( availableInnerHeight ) ) {
childHeight = availableInnerHeight ;
2016-07-20 13:40:26 +00:00
childHeightMeasureMode = CSSMeasureModeAtMost ;
2016-06-15 16:44:30 +00:00
}
}
2016-07-08 21:47:31 +00:00
// If child has no defined size in the cross axis and is set to stretch, set the cross
// axis to be measured exactly with the available inner width
if ( ! isMainAxisRow & &
! isUndefined ( availableInnerWidth ) & &
2016-07-20 13:40:26 +00:00
! isStyleDimDefined ( child , CSSFlexDirectionRow ) & &
widthMeasureMode = = CSSMeasureModeExactly & &
getAlignItem ( node , child ) = = CSSAlignStretch ) {
2016-07-08 21:47:31 +00:00
childWidth = availableInnerWidth ;
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = CSSMeasureModeExactly ;
2016-07-08 21:47:31 +00:00
}
if ( isMainAxisRow & &
! isUndefined ( availableInnerHeight ) & &
2016-07-20 13:40:26 +00:00
! isStyleDimDefined ( child , CSSFlexDirectionColumn ) & &
heightMeasureMode = = CSSMeasureModeExactly & &
getAlignItem ( node , child ) = = CSSAlignStretch ) {
2016-07-08 21:47:31 +00:00
childHeight = availableInnerHeight ;
2016-07-20 13:40:26 +00:00
childHeightMeasureMode = CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
}
// Measure the child
layoutNodeInternal ( child , childWidth , childHeight , direction , childWidthMeasureMode , childHeightMeasureMode , false , " measure " ) ;
2016-07-20 13:40:26 +00:00
child - > layout . flexBasis = fmaxf ( isMainAxisRow ? child - > layout . measuredDimensions [ CSSDimensionWidth ] : child - > layout . measuredDimensions [ CSSDimensionHeight ] , getPaddingAndBorderAxis ( child , mainAxis ) ) ;
2016-06-15 16:44:30 +00:00
}
}
}
// STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
// Indexes of children that represent the first and last items in the line.
int startOfLineIndex = 0 ;
int endOfLineIndex = 0 ;
// Number of lines.
int lineCount = 0 ;
// Accumulated cross dimensions of all lines so far.
float totalLineCrossDim = 0 ;
// Max main dimension of all the lines.
float maxLineMainDim = 0 ;
while ( endOfLineIndex < childCount ) {
// Number of items on the currently line. May be different than the difference
// between start and end indicates because we skip over absolute-positioned items.
int itemsOnLine = 0 ;
// sizeConsumedOnCurrentLine is accumulation of the dimensions and margin
// of all the children on the current line. 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 sizeConsumedOnCurrentLine = 0 ;
float totalFlexGrowFactors = 0 ;
float totalFlexShrinkScaledFactors = 0 ;
i = startOfLineIndex ;
// Maintain a linked list of the child nodes that can shrink and/or grow.
2016-07-20 13:40:26 +00:00
CSSNode * firstRelativeChild = NULL ;
CSSNode * currentRelativeChild = NULL ;
2016-06-15 16:44:30 +00:00
// Add items to the current line until it's full or we run out of items.
while ( i < childCount ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , i ) ;
2016-07-20 13:40:26 +00:00
child - > lineIndex = lineCount ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType ! = CSSPositionTypeAbsolute ) {
float outerFlexBasis = child - > layout . flexBasis + getMarginAxis ( child , mainAxis ) ;
2016-06-15 16:44:30 +00:00
// If this is a multi-line flow and this item pushes us over the available size, we've
// hit the end of the current line. Break out of the loop and lay out the current line.
if ( sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim & & isNodeFlexWrap & & itemsOnLine > 0 ) {
break ;
}
sizeConsumedOnCurrentLine + = outerFlexBasis ;
itemsOnLine + + ;
if ( isFlex ( child ) ) {
totalFlexGrowFactors + = getFlexGrowFactor ( child ) ;
// Unlike the grow factor, the shrink factor is scaled relative to the child
// dimension.
2016-07-20 13:40:26 +00:00
totalFlexShrinkScaledFactors + = getFlexShrinkFactor ( child ) * child - > layout . flexBasis ;
2016-06-15 16:44:30 +00:00
}
// Store a private linked list of children that need to be layed out.
if ( firstRelativeChild = = NULL ) {
firstRelativeChild = child ;
}
if ( currentRelativeChild ! = NULL ) {
2016-07-20 13:40:26 +00:00
currentRelativeChild - > nextChild = child ;
2016-06-15 16:44:30 +00:00
}
currentRelativeChild = child ;
2016-07-20 13:40:26 +00:00
child - > nextChild = NULL ;
2016-06-15 16:44:30 +00:00
}
i + + ;
endOfLineIndex + + ;
}
// If we don't need to measure the cross axis, we can skip the entire flex step.
2016-07-20 13:40:26 +00:00
bool canSkipFlex = ! performLayout & & measureModeCrossDim = = CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
// 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 ;
// STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
// Calculate the remaining available space that needs to be allocated.
// If the main dimension size isn't known, it is computed based on
// the line length, so there's no more space left to distribute.
float remainingFreeSpace = 0 ;
if ( ! isUndefined ( availableInnerMainDim ) ) {
remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine ;
} else if ( sizeConsumedOnCurrentLine < 0 ) {
// availableInnerMainDim is indefinite which means the node is being sized based on its content.
// sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for
// its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.
remainingFreeSpace = - sizeConsumedOnCurrentLine ;
}
float originalRemainingFreeSpace = remainingFreeSpace ;
float deltaFreeSpace = 0 ;
if ( ! canSkipFlex ) {
float childFlexBasis ;
float flexShrinkScaledFactor ;
float flexGrowFactor ;
float baseMainSize ;
float boundMainSize ;
// Do two passes over the flex items to figure out how to distribute the remaining space.
// The first pass finds the items whose min/max constraints trigger, freezes them at those
// sizes, and excludes those sizes from the remaining space. The second pass sets the size
// of each flexible item. It distributes the remaining space amongst the items whose min/max
// constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing
// their min/max constraints to trigger again.
//
// This two pass approach for resolving min/max constraints deviates from the spec. The
// spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process
// that needs to be repeated a variable number of times. The algorithm implemented here
// won't handle all cases but it was simpler to implement and it mitigates performance
// concerns because we know exactly how many passes it'll do.
// First pass: detect the flex items whose min/max constraints trigger
float deltaFlexShrinkScaledFactors = 0 ;
float deltaFlexGrowFactors = 0 ;
currentRelativeChild = firstRelativeChild ;
while ( currentRelativeChild ! = NULL ) {
2016-07-20 13:40:26 +00:00
childFlexBasis = currentRelativeChild - > layout . flexBasis ;
2016-06-15 16:44:30 +00:00
if ( remainingFreeSpace < 0 ) {
flexShrinkScaledFactor = getFlexShrinkFactor ( currentRelativeChild ) * childFlexBasis ;
// Is this child able to shrink?
if ( flexShrinkScaledFactor ! = 0 ) {
baseMainSize = childFlexBasis +
remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor ;
boundMainSize = boundAxis ( currentRelativeChild , mainAxis , baseMainSize ) ;
if ( baseMainSize ! = boundMainSize ) {
// By excluding this item's size and flex factor from remaining, this item's
// min/max constraints should also trigger in the second pass resulting in the
// item's size calculation being identical in the first and second passes.
deltaFreeSpace - = boundMainSize - childFlexBasis ;
deltaFlexShrinkScaledFactors - = flexShrinkScaledFactor ;
}
}
} else if ( remainingFreeSpace > 0 ) {
flexGrowFactor = getFlexGrowFactor ( currentRelativeChild ) ;
// Is this child able to grow?
if ( flexGrowFactor ! = 0 ) {
baseMainSize = childFlexBasis +
remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor ;
boundMainSize = boundAxis ( currentRelativeChild , mainAxis , baseMainSize ) ;
if ( baseMainSize ! = boundMainSize ) {
// By excluding this item's size and flex factor from remaining, this item's
// min/max constraints should also trigger in the second pass resulting in the
// item's size calculation being identical in the first and second passes.
deltaFreeSpace - = boundMainSize - childFlexBasis ;
deltaFlexGrowFactors - = flexGrowFactor ;
}
}
}
2016-07-20 13:40:26 +00:00
currentRelativeChild = currentRelativeChild - > nextChild ;
2016-06-15 16:44:30 +00:00
}
totalFlexShrinkScaledFactors + = deltaFlexShrinkScaledFactors ;
totalFlexGrowFactors + = deltaFlexGrowFactors ;
remainingFreeSpace + = deltaFreeSpace ;
// Second pass: resolve the sizes of the flexible items
deltaFreeSpace = 0 ;
currentRelativeChild = firstRelativeChild ;
while ( currentRelativeChild ! = NULL ) {
2016-07-20 13:40:26 +00:00
childFlexBasis = currentRelativeChild - > layout . flexBasis ;
2016-06-15 16:44:30 +00:00
float updatedMainSize = childFlexBasis ;
if ( remainingFreeSpace < 0 ) {
flexShrinkScaledFactor = getFlexShrinkFactor ( currentRelativeChild ) * childFlexBasis ;
// Is this child able to shrink?
if ( flexShrinkScaledFactor ! = 0 ) {
updatedMainSize = boundAxis ( currentRelativeChild , mainAxis , childFlexBasis +
remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor ) ;
}
} else if ( remainingFreeSpace > 0 ) {
flexGrowFactor = getFlexGrowFactor ( currentRelativeChild ) ;
// Is this child able to grow?
if ( flexGrowFactor ! = 0 ) {
updatedMainSize = boundAxis ( currentRelativeChild , mainAxis , childFlexBasis +
remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor ) ;
}
}
deltaFreeSpace - = updatedMainSize - childFlexBasis ;
if ( isMainAxisRow ) {
2016-07-20 13:40:26 +00:00
childWidth = updatedMainSize + getMarginAxis ( currentRelativeChild , CSSFlexDirectionRow ) ;
childWidthMeasureMode = CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
if ( ! isUndefined ( availableInnerCrossDim ) & &
2016-07-20 13:40:26 +00:00
! isStyleDimDefined ( currentRelativeChild , CSSFlexDirectionColumn ) & &
heightMeasureMode = = CSSMeasureModeExactly & &
getAlignItem ( node , currentRelativeChild ) = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
childHeight = availableInnerCrossDim ;
2016-07-20 13:40:26 +00:00
childHeightMeasureMode = CSSMeasureModeExactly ;
} else if ( ! isStyleDimDefined ( currentRelativeChild , CSSFlexDirectionColumn ) ) {
2016-06-15 16:44:30 +00:00
childHeight = availableInnerCrossDim ;
2016-07-20 13:40:26 +00:00
childHeightMeasureMode = isUndefined ( childHeight ) ? CSSMeasureModeUndefined : CSSMeasureModeAtMost ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
childHeight = currentRelativeChild - > style . dimensions [ CSSDimensionHeight ] + getMarginAxis ( currentRelativeChild , CSSFlexDirectionColumn ) ;
childHeightMeasureMode = CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
}
} else {
2016-07-20 13:40:26 +00:00
childHeight = updatedMainSize + getMarginAxis ( currentRelativeChild , CSSFlexDirectionColumn ) ;
childHeightMeasureMode = CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
if ( ! isUndefined ( availableInnerCrossDim ) & &
2016-07-20 13:40:26 +00:00
! isStyleDimDefined ( currentRelativeChild , CSSFlexDirectionRow ) & &
widthMeasureMode = = CSSMeasureModeExactly & &
getAlignItem ( node , currentRelativeChild ) = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
childWidth = availableInnerCrossDim ;
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = CSSMeasureModeExactly ;
} else if ( ! isStyleDimDefined ( currentRelativeChild , CSSFlexDirectionRow ) ) {
2016-06-15 16:44:30 +00:00
childWidth = availableInnerCrossDim ;
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = isUndefined ( childWidth ) ? CSSMeasureModeUndefined : CSSMeasureModeAtMost ;
2016-06-15 16:44:30 +00:00
} else {
2016-07-20 13:40:26 +00:00
childWidth = currentRelativeChild - > style . dimensions [ CSSDimensionWidth ] + getMarginAxis ( currentRelativeChild , CSSFlexDirectionRow ) ;
childWidthMeasureMode = CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
}
}
bool requiresStretchLayout = ! isStyleDimDefined ( currentRelativeChild , crossAxis ) & &
2016-07-20 13:40:26 +00:00
getAlignItem ( node , currentRelativeChild ) = = CSSAlignStretch ;
2016-06-15 16:44:30 +00:00
// Recursively call the layout algorithm for this child with the updated main size.
layoutNodeInternal ( currentRelativeChild , childWidth , childHeight , direction , childWidthMeasureMode , childHeightMeasureMode , performLayout & & ! requiresStretchLayout , " flex " ) ;
2016-07-20 13:40:26 +00:00
currentRelativeChild = currentRelativeChild - > nextChild ;
2016-06-15 16:44:30 +00:00
}
}
remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace ;
// STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
// At this point, all the children have their dimensions set in the main axis.
// Their dimensions are also set in the cross axis with the exception of items
// that are aligned "stretch". We need to compute these stretch values and
// set the final positions.
// If we are using "at most" rules in the main axis, we won't distribute
// any remaining space at this point.
2016-07-20 13:40:26 +00:00
if ( measureModeMainDim = = CSSMeasureModeAtMost ) {
2016-06-15 16:44:30 +00:00
remainingFreeSpace = 0 ;
}
// Use justifyContent to figure out how to allocate the remaining space
// available in the main axis.
2016-07-20 13:40:26 +00:00
if ( justifyContent ! = CSSJustifyFlexStart ) {
if ( justifyContent = = CSSJustifyCenter ) {
2016-06-15 16:44:30 +00:00
leadingMainDim = remainingFreeSpace / 2 ;
2016-07-20 13:40:26 +00:00
} else if ( justifyContent = = CSSJustifyFlexEnd ) {
2016-06-15 16:44:30 +00:00
leadingMainDim = remainingFreeSpace ;
2016-07-20 13:40:26 +00:00
} else if ( justifyContent = = CSSJustifySpaceBetween ) {
2016-06-15 16:44:30 +00:00
remainingFreeSpace = fmaxf ( remainingFreeSpace , 0 ) ;
if ( itemsOnLine > 1 ) {
betweenMainDim = remainingFreeSpace / ( itemsOnLine - 1 ) ;
} else {
betweenMainDim = 0 ;
}
2016-07-20 13:40:26 +00:00
} else if ( justifyContent = = CSSJustifySpaceAround ) {
2016-06-15 16:44:30 +00:00
// Space on the edges is half of the space between elements
betweenMainDim = remainingFreeSpace / itemsOnLine ;
leadingMainDim = betweenMainDim / 2 ;
}
}
float mainDim = leadingPaddingAndBorderMain + leadingMainDim ;
float crossDim = 0 ;
for ( i = startOfLineIndex ; i < endOfLineIndex ; + + i ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , i ) ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType = = CSSPositionTypeAbsolute & &
2016-06-15 16:44:30 +00:00
isPosDefined ( child , leading [ mainAxis ] ) ) {
if ( performLayout ) {
// 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 ] ] = getPosition ( child , leading [ mainAxis ] ) +
getLeadingBorder ( node , mainAxis ) +
getLeadingMargin ( child , mainAxis ) ;
}
} else {
if ( performLayout ) {
// 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 ;
}
// Now that we placed the element, we need to update the variables.
// We need to do that only for relative elements. Absolute elements
// do not take part in that phase.
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType = = CSSPositionTypeRelative ) {
2016-06-15 16:44:30 +00:00
if ( canSkipFlex ) {
// If we skipped the flex step, then we can't rely on the measuredDims because
// they weren't computed. This means we can't call getDimWithMargin.
2016-07-20 13:40:26 +00:00
mainDim + = betweenMainDim + getMarginAxis ( child , mainAxis ) + child - > layout . flexBasis ;
2016-06-15 16:44:30 +00:00
crossDim = availableInnerCrossDim ;
} else {
// The main dimension is the sum of all the elements dimension plus
// the spacing.
mainDim + = betweenMainDim + getDimWithMargin ( child , mainAxis ) ;
// The cross dimension is the max of the elements dimension since there
// can only be one element in that cross dimension.
crossDim = fmaxf ( crossDim , getDimWithMargin ( child , crossAxis ) ) ;
}
}
}
}
mainDim + = trailingPaddingAndBorderMain ;
float containerCrossAxis = availableInnerCrossDim ;
2016-07-20 13:40:26 +00:00
if ( measureModeCrossDim = = CSSMeasureModeUndefined | | measureModeCrossDim = = CSSMeasureModeAtMost ) {
2016-06-15 16:44:30 +00:00
// Compute the cross axis from the max cross dimension of the children.
containerCrossAxis = boundAxis ( node , crossAxis , crossDim + paddingAndBorderAxisCross ) - paddingAndBorderAxisCross ;
2016-07-20 13:40:26 +00:00
if ( measureModeCrossDim = = CSSMeasureModeAtMost ) {
2016-06-15 16:44:30 +00:00
containerCrossAxis = fminf ( containerCrossAxis , availableInnerCrossDim ) ;
}
}
// If there's no flex wrap, the cross dimension is defined by the container.
2016-07-20 13:40:26 +00:00
if ( ! isNodeFlexWrap & & measureModeCrossDim = = CSSMeasureModeExactly ) {
2016-06-15 16:44:30 +00:00
crossDim = availableInnerCrossDim ;
}
// Clamp to the min/max size specified on the container.
crossDim = boundAxis ( node , crossAxis , crossDim + paddingAndBorderAxisCross ) - paddingAndBorderAxisCross ;
// STEP 7: CROSS-AXIS ALIGNMENT
// We can skip child alignment if we're just measuring the container.
if ( performLayout ) {
for ( i = startOfLineIndex ; i < endOfLineIndex ; + + i ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , i ) ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType = = CSSPositionTypeAbsolute ) {
2016-06-15 16:44:30 +00:00
// If the child is absolutely positioned and has a top/left/bottom/right
// set, override all the previously computed positions to set it correctly.
if ( isPosDefined ( child , leading [ crossAxis ] ) ) {
child - > layout . position [ pos [ crossAxis ] ] = getPosition ( child , leading [ crossAxis ] ) +
getLeadingBorder ( node , crossAxis ) +
getLeadingMargin ( child , crossAxis ) ;
} else {
child - > layout . position [ pos [ crossAxis ] ] = leadingPaddingAndBorderCross +
getLeadingMargin ( child , 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
2016-07-20 13:40:26 +00:00
CSSAlign alignItem = getAlignItem ( node , child ) ;
2016-06-15 16:44:30 +00:00
// If the child uses align stretch, we need to lay it out one more time, this time
// forcing the cross-axis size to be the computed cross size for the current line.
2016-07-20 13:40:26 +00:00
if ( alignItem = = CSSAlignStretch ) {
childWidth = child - > layout . measuredDimensions [ CSSDimensionWidth ] + getMarginAxis ( child , CSSFlexDirectionRow ) ;
childHeight = child - > layout . measuredDimensions [ CSSDimensionHeight ] + getMarginAxis ( child , CSSFlexDirectionColumn ) ;
2016-06-15 16:44:30 +00:00
bool isCrossSizeDefinite = false ;
if ( isMainAxisRow ) {
2016-07-20 13:40:26 +00:00
isCrossSizeDefinite = isStyleDimDefined ( child , CSSFlexDirectionColumn ) ;
2016-06-15 16:44:30 +00:00
childHeight = crossDim ;
} else {
2016-07-20 13:40:26 +00:00
isCrossSizeDefinite = isStyleDimDefined ( child , CSSFlexDirectionRow ) ;
2016-06-15 16:44:30 +00:00
childWidth = crossDim ;
}
// If the child defines a definite size for its cross axis, there's no need to stretch.
if ( ! isCrossSizeDefinite ) {
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = isUndefined ( childWidth ) ? CSSMeasureModeUndefined : CSSMeasureModeExactly ;
childHeightMeasureMode = isUndefined ( childHeight ) ? CSSMeasureModeUndefined : CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
layoutNodeInternal ( child , childWidth , childHeight , direction , childWidthMeasureMode , childHeightMeasureMode , true , " stretch " ) ;
}
2016-07-20 13:40:26 +00:00
} else if ( alignItem ! = CSSAlignFlexStart ) {
2016-06-15 16:44:30 +00:00
float remainingCrossDim = containerCrossAxis - getDimWithMargin ( child , crossAxis ) ;
2016-07-20 13:40:26 +00:00
if ( alignItem = = CSSAlignCenter ) {
2016-06-15 16:44:30 +00:00
leadingCrossDim + = remainingCrossDim / 2 ;
2016-07-20 13:40:26 +00:00
} else { // CSSAlignFlexEnd
2016-06-15 16:44:30 +00:00
leadingCrossDim + = remainingCrossDim ;
}
}
// And we apply the position
child - > layout . position [ pos [ crossAxis ] ] + = totalLineCrossDim + leadingCrossDim ;
}
}
}
totalLineCrossDim + = crossDim ;
maxLineMainDim = fmaxf ( maxLineMainDim , mainDim ) ;
// Reset variables for new line.
lineCount + + ;
startOfLineIndex = endOfLineIndex ;
endOfLineIndex = startOfLineIndex ;
}
// STEP 8: MULTI-LINE CONTENT ALIGNMENT
if ( lineCount > 1 & & performLayout & & ! isUndefined ( availableInnerCrossDim ) ) {
float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim ;
float crossDimLead = 0 ;
float currentLead = leadingPaddingAndBorderCross ;
2016-07-20 13:40:26 +00:00
CSSAlign alignContent = node - > style . alignContent ;
if ( alignContent = = CSSAlignFlexEnd ) {
2016-06-15 16:44:30 +00:00
currentLead + = remainingAlignContentDim ;
2016-07-20 13:40:26 +00:00
} else if ( alignContent = = CSSAlignCenter ) {
2016-06-15 16:44:30 +00:00
currentLead + = remainingAlignContentDim / 2 ;
2016-07-20 13:40:26 +00:00
} else if ( alignContent = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
if ( availableInnerCrossDim > totalLineCrossDim ) {
crossDimLead = ( remainingAlignContentDim / lineCount ) ;
}
}
int endIndex = 0 ;
for ( i = 0 ; i < lineCount ; + + i ) {
int startIndex = endIndex ;
int j ;
// compute the line's height and find the endIndex
float lineHeight = 0 ;
for ( j = startIndex ; j < childCount ; + + j ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , j ) ;
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType ! = CSSPositionTypeRelative ) {
2016-06-15 16:44:30 +00:00
continue ;
}
2016-07-20 13:40:26 +00:00
if ( child - > lineIndex ! = i ) {
2016-06-15 16:44:30 +00:00
break ;
}
if ( isLayoutDimDefined ( child , crossAxis ) ) {
lineHeight = fmaxf ( lineHeight ,
2016-07-20 13:40:26 +00:00
child - > layout . measuredDimensions [ dim [ crossAxis ] ] + getMarginAxis ( child , crossAxis ) ) ;
2016-06-15 16:44:30 +00:00
}
}
endIndex = j ;
lineHeight + = crossDimLead ;
if ( performLayout ) {
for ( j = startIndex ; j < endIndex ; + + j ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , j ) ;
2016-07-20 13:40:26 +00:00
if ( child - > style . positionType ! = CSSPositionTypeRelative ) {
2016-06-15 16:44:30 +00:00
continue ;
}
2016-07-20 13:40:26 +00:00
CSSAlign alignContentAlignItem = getAlignItem ( node , child ) ;
if ( alignContentAlignItem = = CSSAlignFlexStart ) {
2016-06-15 16:44:30 +00:00
child - > layout . position [ pos [ crossAxis ] ] = currentLead + getLeadingMargin ( child , crossAxis ) ;
2016-07-20 13:40:26 +00:00
} else if ( alignContentAlignItem = = CSSAlignFlexEnd ) {
child - > layout . position [ pos [ crossAxis ] ] = currentLead + lineHeight - getTrailingMargin ( child , crossAxis ) - child - > layout . measuredDimensions [ dim [ crossAxis ] ] ;
} else if ( alignContentAlignItem = = CSSAlignCenter ) {
childHeight = child - > layout . measuredDimensions [ dim [ crossAxis ] ] ;
2016-06-15 16:44:30 +00:00
child - > layout . position [ pos [ crossAxis ] ] = currentLead + ( lineHeight - childHeight ) / 2 ;
2016-07-20 13:40:26 +00:00
} else if ( alignContentAlignItem = = CSSAlignStretch ) {
2016-06-15 16:44:30 +00:00
child - > layout . position [ pos [ crossAxis ] ] = currentLead + getLeadingMargin ( child , crossAxis ) ;
// TODO(prenaux): Correctly set the height of items with indefinite
// (auto) crossAxis dimension.
}
}
}
currentLead + = lineHeight ;
}
}
// STEP 9: COMPUTING FINAL DIMENSIONS
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ CSSDimensionWidth ] = boundAxis ( node , CSSFlexDirectionRow , availableWidth - marginAxisRow ) ;
node - > layout . measuredDimensions [ CSSDimensionHeight ] = boundAxis ( node , CSSFlexDirectionColumn , availableHeight - marginAxisColumn ) ;
2016-06-15 16:44:30 +00:00
// If the user didn't specify a width or height for the node, set the
// dimensions based on the children.
2016-07-20 13:40:26 +00:00
if ( measureModeMainDim = = CSSMeasureModeUndefined ) {
2016-06-15 16:44:30 +00:00
// Clamp the size to the min/max size, if specified, and make sure it
// doesn't go below the padding and border amount.
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ dim [ mainAxis ] ] = boundAxis ( node , mainAxis , maxLineMainDim ) ;
} else if ( measureModeMainDim = = CSSMeasureModeAtMost ) {
node - > layout . measuredDimensions [ dim [ mainAxis ] ] = fmaxf (
2016-06-15 16:44:30 +00:00
fminf ( availableInnerMainDim + paddingAndBorderAxisMain ,
boundAxisWithinMinAndMax ( node , mainAxis , maxLineMainDim ) ) ,
paddingAndBorderAxisMain ) ;
}
2016-07-20 13:40:26 +00:00
if ( measureModeCrossDim = = CSSMeasureModeUndefined ) {
2016-06-15 16:44:30 +00:00
// Clamp the size to the min/max size, if specified, and make sure it
// doesn't go below the padding and border amount.
2016-07-20 13:40:26 +00:00
node - > layout . measuredDimensions [ dim [ crossAxis ] ] = boundAxis ( node , crossAxis , totalLineCrossDim + paddingAndBorderAxisCross ) ;
} else if ( measureModeCrossDim = = CSSMeasureModeAtMost ) {
node - > layout . measuredDimensions [ dim [ crossAxis ] ] = fmaxf (
2016-06-15 16:44:30 +00:00
fminf ( availableInnerCrossDim + paddingAndBorderAxisCross ,
boundAxisWithinMinAndMax ( node , crossAxis , totalLineCrossDim + paddingAndBorderAxisCross ) ) ,
paddingAndBorderAxisCross ) ;
}
// STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN
if ( performLayout ) {
bool needsMainTrailingPos = false ;
bool needsCrossTrailingPos = false ;
2016-07-20 13:40:26 +00:00
if ( mainAxis = = CSSFlexDirectionRowReverse | |
mainAxis = = CSSFlexDirectionColumnReverse ) {
2016-06-15 16:44:30 +00:00
needsMainTrailingPos = true ;
}
2016-07-20 13:40:26 +00:00
if ( crossAxis = = CSSFlexDirectionRowReverse | |
crossAxis = = CSSFlexDirectionColumnReverse ) {
2016-06-15 16:44:30 +00:00
needsCrossTrailingPos = true ;
}
// Set trailing position if necessary.
if ( needsMainTrailingPos | | needsCrossTrailingPos ) {
for ( i = 0 ; i < childCount ; + + i ) {
2016-07-20 20:26:57 +00:00
child = CSSNodeListGet ( node - > children , i ) ;
2016-06-15 16:44:30 +00:00
if ( needsMainTrailingPos ) {
setTrailingPosition ( node , child , mainAxis ) ;
}
if ( needsCrossTrailingPos ) {
setTrailingPosition ( node , child , crossAxis ) ;
}
}
}
}
// STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN
currentAbsoluteChild = firstAbsoluteChild ;
while ( currentAbsoluteChild ! = NULL ) {
// Now that we know the bounds of the container, perform layout again on the
// absolutely-positioned children.
if ( performLayout ) {
2016-07-20 13:40:26 +00:00
childWidth = CSSUndefined ;
childHeight = CSSUndefined ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( isStyleDimDefined ( currentAbsoluteChild , CSSFlexDirectionRow ) ) {
childWidth = currentAbsoluteChild - > style . dimensions [ CSSDimensionWidth ] + getMarginAxis ( currentAbsoluteChild , CSSFlexDirectionRow ) ;
2016-06-15 16:44:30 +00:00
} else {
// If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.
2016-07-20 13:40:26 +00:00
if ( isPosDefined ( currentAbsoluteChild , CSSPositionLeft ) & & isPosDefined ( currentAbsoluteChild , CSSPositionRight ) ) {
childWidth = node - > layout . measuredDimensions [ CSSDimensionWidth ] -
( getLeadingBorder ( node , CSSFlexDirectionRow ) + getTrailingBorder ( node , CSSFlexDirectionRow ) ) -
( currentAbsoluteChild - > style . position [ CSSPositionLeft ] + currentAbsoluteChild - > style . position [ CSSPositionRight ] ) ;
childWidth = boundAxis ( currentAbsoluteChild , CSSFlexDirectionRow , childWidth ) ;
2016-06-15 16:44:30 +00:00
}
}
2016-07-20 13:40:26 +00:00
if ( isStyleDimDefined ( currentAbsoluteChild , CSSFlexDirectionColumn ) ) {
childHeight = currentAbsoluteChild - > style . dimensions [ CSSDimensionHeight ] + getMarginAxis ( currentAbsoluteChild , CSSFlexDirectionColumn ) ;
2016-06-15 16:44:30 +00:00
} else {
// If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.
2016-07-20 13:40:26 +00:00
if ( isPosDefined ( currentAbsoluteChild , CSSPositionTop ) & & isPosDefined ( currentAbsoluteChild , CSSPositionBottom ) ) {
childHeight = node - > layout . measuredDimensions [ CSSDimensionHeight ] -
( getLeadingBorder ( node , CSSFlexDirectionColumn ) + getTrailingBorder ( node , CSSFlexDirectionColumn ) ) -
( currentAbsoluteChild - > style . position [ CSSPositionTop ] + currentAbsoluteChild - > style . position [ CSSPositionBottom ] ) ;
childHeight = boundAxis ( currentAbsoluteChild , CSSFlexDirectionColumn , childHeight ) ;
2016-06-15 16:44:30 +00:00
}
}
// If we're still missing one or the other dimension, measure the content.
if ( isUndefined ( childWidth ) | | isUndefined ( childHeight ) ) {
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = isUndefined ( childWidth ) ? CSSMeasureModeUndefined : CSSMeasureModeExactly ;
childHeightMeasureMode = isUndefined ( childHeight ) ? CSSMeasureModeUndefined : CSSMeasureModeExactly ;
2016-06-15 16:44:30 +00:00
// According to the spec, if the main size is not definite and the
// child's inline axis is parallel to the main axis (i.e. it's
// horizontal), the child should be sized using "UNDEFINED" in
// the main size. Otherwise use "AT_MOST" in the cross axis.
if ( ! isMainAxisRow & & isUndefined ( childWidth ) & & ! isUndefined ( availableInnerWidth ) ) {
childWidth = availableInnerWidth ;
2016-07-20 13:40:26 +00:00
childWidthMeasureMode = CSSMeasureModeAtMost ;
2016-06-15 16:44:30 +00:00
}
// The W3C spec doesn't say anything about the 'overflow' property,
// but all major browsers appear to implement the following logic.
2016-07-20 13:40:26 +00:00
if ( node - > style . overflow = = CSSOverflowHidden ) {
2016-06-15 16:44:30 +00:00
if ( isMainAxisRow & & isUndefined ( childHeight ) & & ! isUndefined ( availableInnerHeight ) ) {
childHeight = availableInnerHeight ;
2016-07-20 13:40:26 +00:00
childHeightMeasureMode = CSSMeasureModeAtMost ;
2016-06-15 16:44:30 +00:00
}
}
layoutNodeInternal ( currentAbsoluteChild , childWidth , childHeight , direction , childWidthMeasureMode , childHeightMeasureMode , false , " abs-measure " ) ;
2016-07-20 13:40:26 +00:00
childWidth = currentAbsoluteChild - > layout . measuredDimensions [ CSSDimensionWidth ] + getMarginAxis ( currentAbsoluteChild , CSSFlexDirectionRow ) ;
childHeight = currentAbsoluteChild - > layout . measuredDimensions [ CSSDimensionHeight ] + getMarginAxis ( currentAbsoluteChild , CSSFlexDirectionColumn ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
layoutNodeInternal ( currentAbsoluteChild , childWidth , childHeight , direction , CSSMeasureModeExactly , CSSMeasureModeExactly , true , " abs-layout " ) ;
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
if ( isPosDefined ( currentAbsoluteChild , trailing [ CSSFlexDirectionRow ] ) & &
! isPosDefined ( currentAbsoluteChild , leading [ CSSFlexDirectionRow ] ) ) {
currentAbsoluteChild - > layout . position [ leading [ CSSFlexDirectionRow ] ] =
node - > layout . measuredDimensions [ dim [ CSSFlexDirectionRow ] ] -
currentAbsoluteChild - > layout . measuredDimensions [ dim [ CSSFlexDirectionRow ] ] -
getPosition ( currentAbsoluteChild , trailing [ CSSFlexDirectionRow ] ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
if ( isPosDefined ( currentAbsoluteChild , trailing [ CSSFlexDirectionColumn ] ) & &
! isPosDefined ( currentAbsoluteChild , leading [ CSSFlexDirectionColumn ] ) ) {
currentAbsoluteChild - > layout . position [ leading [ CSSFlexDirectionColumn ] ] =
node - > layout . measuredDimensions [ dim [ CSSFlexDirectionColumn ] ] -
currentAbsoluteChild - > layout . measuredDimensions [ dim [ CSSFlexDirectionColumn ] ] -
getPosition ( currentAbsoluteChild , trailing [ CSSFlexDirectionColumn ] ) ;
2016-06-15 16:44:30 +00:00
}
}
2016-07-20 13:40:26 +00:00
currentAbsoluteChild = currentAbsoluteChild - > nextChild ;
2016-06-15 16:44:30 +00:00
}
}
int gDepth = 0 ;
bool gPrintTree = false ;
bool gPrintChanges = false ;
bool gPrintSkips = false ;
static const char * spacer = " " ;
static const char * getSpacer ( unsigned long level ) {
unsigned long spacerLen = strlen ( spacer ) ;
if ( level > spacerLen ) {
level = spacerLen ;
}
return & spacer [ spacerLen - level ] ;
}
2016-07-20 13:40:26 +00:00
static const char * getModeName ( CSSMeasureMode mode , bool performLayout ) {
const char * kMeasureModeNames [ CSSMeasureModeCount ] = {
2016-06-15 16:44:30 +00:00
" UNDEFINED " ,
" EXACTLY " ,
" AT_MOST "
} ;
2016-07-20 13:40:26 +00:00
const char * kLayoutModeNames [ CSSMeasureModeCount ] = {
2016-06-15 16:44:30 +00:00
" LAY_UNDEFINED " ,
" LAY_EXACTLY " ,
" LAY_AT_MOST "
} ;
2016-07-20 13:40:26 +00:00
if ( mode > = CSSMeasureModeCount ) {
2016-06-15 16:44:30 +00:00
return " " ;
}
return performLayout ? kLayoutModeNames [ mode ] : kMeasureModeNames [ mode ] ;
}
static bool canUseCachedMeasurement (
2016-07-20 13:40:26 +00:00
bool isTextNode ,
float availableWidth ,
float availableHeight ,
2016-06-15 16:44:30 +00:00
float margin_row ,
float margin_column ,
2016-07-20 13:40:26 +00:00
CSSMeasureMode widthMeasureMode ,
CSSMeasureMode heightMeasureMode ,
CSSCachedMeasurement cached_layout ) {
2016-06-15 16:44:30 +00:00
bool is_height_same =
2016-07-20 13:40:26 +00:00
( cached_layout . heightMeasureMode = = CSSMeasureModeUndefined & & heightMeasureMode = = CSSMeasureModeUndefined ) | |
( cached_layout . heightMeasureMode = = heightMeasureMode & & eq ( cached_layout . availableHeight , availableHeight ) ) ;
2016-06-15 16:44:30 +00:00
bool is_width_same =
2016-07-20 13:40:26 +00:00
( cached_layout . widthMeasureMode = = CSSMeasureModeUndefined & & widthMeasureMode = = CSSMeasureModeUndefined ) | |
( cached_layout . widthMeasureMode = = widthMeasureMode & & eq ( cached_layout . availableWidth , availableWidth ) ) ;
2016-06-15 16:44:30 +00:00
if ( is_height_same & & is_width_same ) {
return true ;
}
bool is_height_valid =
2016-07-20 13:40:26 +00:00
( cached_layout . heightMeasureMode = = CSSMeasureModeUndefined & & heightMeasureMode = = CSSMeasureModeAtMost & & cached_layout . computedHeight < = ( availableHeight - margin_column ) ) | |
( heightMeasureMode = = CSSMeasureModeExactly & & eq ( cached_layout . computedHeight , availableHeight - margin_column ) ) ;
2016-06-15 16:44:30 +00:00
if ( is_width_same & & is_height_valid ) {
return true ;
}
bool is_width_valid =
2016-07-20 13:40:26 +00:00
( cached_layout . widthMeasureMode = = CSSMeasureModeUndefined & & widthMeasureMode = = CSSMeasureModeAtMost & & cached_layout . computedWidth < = ( availableWidth - margin_row ) ) | |
( widthMeasureMode = = CSSMeasureModeExactly & & eq ( cached_layout . computedWidth , availableWidth - margin_row ) ) ;
2016-06-15 16:44:30 +00:00
if ( is_height_same & & is_width_valid ) {
return true ;
}
if ( is_height_valid & & is_width_valid ) {
return true ;
}
// We know this to be text so we can apply some more specialized heuristics.
2016-07-20 13:40:26 +00:00
if ( isTextNode ) {
2016-06-15 16:44:30 +00:00
if ( is_width_same ) {
2016-07-20 13:40:26 +00:00
if ( heightMeasureMode = = CSSMeasureModeUndefined ) {
2016-06-15 16:44:30 +00:00
// Width is the same and height is not restricted. Re-use cahced value.
return true ;
}
2016-07-20 13:40:26 +00:00
if ( heightMeasureMode = = CSSMeasureModeAtMost & &
cached_layout . computedHeight < ( availableHeight - margin_column ) ) {
2016-06-15 16:44:30 +00:00
// Width is the same and height restriction is greater than the cached height. Re-use cached value.
return true ;
}
// Width is the same but height restriction imposes smaller height than previously measured.
// Update the cached value to respect the new height restriction.
2016-07-20 13:40:26 +00:00
cached_layout . computedHeight = availableHeight - margin_column ;
2016-06-15 16:44:30 +00:00
return true ;
}
2016-07-20 13:40:26 +00:00
if ( cached_layout . widthMeasureMode = = CSSMeasureModeUndefined ) {
if ( widthMeasureMode = = CSSMeasureModeUndefined | |
( widthMeasureMode = = CSSMeasureModeAtMost & &
cached_layout . computedWidth < = ( availableWidth - margin_row ) ) ) {
2016-06-15 16:44:30 +00:00
// Previsouly this text was measured with no width restriction, if width is now restricted
// but to a larger value than the previsouly measured width we can re-use the measurement
// as we know it will fit.
return true ;
}
}
}
return false ;
}
//
// This is a wrapper around the layoutNodeImpl function. It determines
// whether the layout request is redundant and can be skipped.
//
// Parameters:
// Input parameters are the same as layoutNodeImpl (see above)
// Return parameter is true if layout was performed, false if skipped
//
2016-07-20 13:40:26 +00:00
bool layoutNodeInternal ( CSSNode * node , float availableWidth , float availableHeight ,
CSSDirection parentDirection , CSSMeasureMode widthMeasureMode , CSSMeasureMode heightMeasureMode , bool performLayout , char * reason ) {
CSSLayout * layout = & node - > layout ;
2016-06-15 16:44:30 +00:00
gDepth + + ;
2016-07-22 18:36:15 +00:00
bool needToVisitNode = ( node - > isDirty & & layout - > generationCount ! = gCurrentGenerationCount ) | |
2016-07-20 13:40:26 +00:00
layout - > lastParentDirection ! = parentDirection ;
2016-06-15 16:44:30 +00:00
if ( needToVisitNode ) {
// Invalidate the cached results.
2016-07-20 13:40:26 +00:00
layout - > nextCachedMeasurementsIndex = 0 ;
layout - > cached_layout . widthMeasureMode = ( CSSMeasureMode ) - 1 ;
layout - > cached_layout . heightMeasureMode = ( CSSMeasureMode ) - 1 ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
CSSCachedMeasurement * cachedResults = NULL ;
2016-06-15 16:44:30 +00:00
// Determine whether the results are already cached. We maintain a separate
// cache for layouts and measurements. A layout operation modifies the positions
// and dimensions for nodes in the subtree. The algorithm assumes that each node
// gets layed out a maximum of one time per tree layout, but multiple measurements
// may be required to resolve all of the flex dimensions.
// We handle nodes with measure functions specially here because they are the most
// expensive to measure, so it's worth avoiding redundant measurements if at all possible.
if ( isMeasureDefined ( node ) ) {
2016-07-20 13:40:26 +00:00
float marginAxisRow = getMarginAxis ( node , CSSFlexDirectionRow ) ;
float marginAxisColumn = getMarginAxis ( node , CSSFlexDirectionColumn ) ;
2016-06-15 16:44:30 +00:00
// First, try to use the layout cache.
2016-07-20 15:46:04 +00:00
if ( canUseCachedMeasurement ( node - > isTextNode , availableWidth , availableHeight , marginAxisRow , marginAxisColumn ,
2016-06-15 16:44:30 +00:00
widthMeasureMode , heightMeasureMode , layout - > cached_layout ) ) {
cachedResults = & layout - > cached_layout ;
} else {
// Try to use the measurement cache.
2016-07-20 13:40:26 +00:00
for ( int i = 0 ; i < layout - > nextCachedMeasurementsIndex ; i + + ) {
2016-07-20 15:46:04 +00:00
if ( canUseCachedMeasurement ( node - > isTextNode , availableWidth , availableHeight , marginAxisRow , marginAxisColumn ,
2016-07-20 13:40:26 +00:00
widthMeasureMode , heightMeasureMode , layout - > cachedMeasurements [ i ] ) ) {
cachedResults = & layout - > cachedMeasurements [ i ] ;
2016-06-15 16:44:30 +00:00
break ;
}
}
}
} else if ( performLayout ) {
2016-07-20 13:40:26 +00:00
if ( eq ( layout - > cached_layout . availableWidth , availableWidth ) & &
eq ( layout - > cached_layout . availableHeight , availableHeight ) & &
layout - > cached_layout . widthMeasureMode = = widthMeasureMode & &
layout - > cached_layout . heightMeasureMode = = heightMeasureMode ) {
2016-06-15 16:44:30 +00:00
cachedResults = & layout - > cached_layout ;
}
} else {
2016-07-20 13:40:26 +00:00
for ( int i = 0 ; i < layout - > nextCachedMeasurementsIndex ; i + + ) {
if ( eq ( layout - > cachedMeasurements [ i ] . availableWidth , availableWidth ) & &
eq ( layout - > cachedMeasurements [ i ] . availableHeight , availableHeight ) & &
layout - > cachedMeasurements [ i ] . widthMeasureMode = = widthMeasureMode & &
layout - > cachedMeasurements [ i ] . heightMeasureMode = = heightMeasureMode ) {
2016-06-15 16:44:30 +00:00
2016-07-20 13:40:26 +00:00
cachedResults = & layout - > cachedMeasurements [ i ] ;
2016-06-15 16:44:30 +00:00
break ;
}
}
}
if ( ! needToVisitNode & & cachedResults ! = NULL ) {
2016-07-20 13:40:26 +00:00
layout - > measuredDimensions [ CSSDimensionWidth ] = cachedResults - > computedWidth ;
layout - > measuredDimensions [ CSSDimensionHeight ] = cachedResults - > computedHeight ;
2016-06-15 16:44:30 +00:00
if ( gPrintChanges & & gPrintSkips ) {
printf ( " %s%d.{[skipped] " , getSpacer ( gDepth ) , gDepth ) ;
if ( node - > print ) {
node - > print ( node - > context ) ;
}
printf ( " wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s \n " ,
getModeName ( widthMeasureMode , performLayout ) ,
getModeName ( heightMeasureMode , performLayout ) ,
availableWidth , availableHeight ,
2016-07-20 13:40:26 +00:00
cachedResults - > computedWidth , cachedResults - > computedHeight , reason ) ;
2016-06-15 16:44:30 +00:00
}
} else {
if ( gPrintChanges ) {
printf ( " %s%d.{%s " , getSpacer ( gDepth ) , gDepth , needToVisitNode ? " * " : " " ) ;
if ( node - > print ) {
node - > print ( node - > context ) ;
}
printf ( " wm: %s, hm: %s, aw: %f ah: %f %s \n " ,
getModeName ( widthMeasureMode , performLayout ) ,
getModeName ( heightMeasureMode , performLayout ) ,
availableWidth , availableHeight , reason ) ;
}
layoutNodeImpl ( node , availableWidth , availableHeight , parentDirection , widthMeasureMode , heightMeasureMode , performLayout ) ;
if ( gPrintChanges ) {
printf ( " %s%d.}%s " , getSpacer ( gDepth ) , gDepth , needToVisitNode ? " * " : " " ) ;
if ( node - > print ) {
node - > print ( node - > context ) ;
}
printf ( " wm: %s, hm: %s, d: (%f, %f) %s \n " ,
getModeName ( widthMeasureMode , performLayout ) ,
getModeName ( heightMeasureMode , performLayout ) ,
2016-07-20 13:40:26 +00:00
layout - > measuredDimensions [ CSSDimensionWidth ] , layout - > measuredDimensions [ CSSDimensionHeight ] , reason ) ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
layout - > lastParentDirection = parentDirection ;
2016-06-15 16:44:30 +00:00
if ( cachedResults = = NULL ) {
2016-07-20 13:40:26 +00:00
if ( layout - > nextCachedMeasurementsIndex = = CSS_MAX_CACHED_RESULT_COUNT ) {
2016-06-15 16:44:30 +00:00
if ( gPrintChanges ) {
printf ( " Out of cache entries! \n " ) ;
}
2016-07-20 13:40:26 +00:00
layout - > nextCachedMeasurementsIndex = 0 ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
CSSCachedMeasurement * newCacheEntry ;
2016-06-15 16:44:30 +00:00
if ( performLayout ) {
// Use the single layout cache entry.
newCacheEntry = & layout - > cached_layout ;
} else {
// Allocate a new measurement cache entry.
2016-07-20 13:40:26 +00:00
newCacheEntry = & layout - > cachedMeasurements [ layout - > nextCachedMeasurementsIndex ] ;
layout - > nextCachedMeasurementsIndex + + ;
2016-06-15 16:44:30 +00:00
}
2016-07-20 13:40:26 +00:00
newCacheEntry - > availableWidth = availableWidth ;
newCacheEntry - > availableHeight = availableHeight ;
newCacheEntry - > widthMeasureMode = widthMeasureMode ;
newCacheEntry - > heightMeasureMode = heightMeasureMode ;
newCacheEntry - > computedWidth = layout - > measuredDimensions [ CSSDimensionWidth ] ;
newCacheEntry - > computedHeight = layout - > measuredDimensions [ CSSDimensionHeight ] ;
2016-06-15 16:44:30 +00:00
}
}
if ( performLayout ) {
2016-07-20 13:40:26 +00:00
node - > layout . dimensions [ CSSDimensionWidth ] = node - > layout . measuredDimensions [ CSSDimensionWidth ] ;
node - > layout . dimensions [ CSSDimensionHeight ] = node - > layout . measuredDimensions [ CSSDimensionHeight ] ;
2016-07-20 15:46:00 +00:00
node - > shouldUpdate = true ;
2016-06-15 16:44:30 +00:00
}
gDepth - - ;
2016-07-20 13:40:26 +00:00
layout - > generationCount = gCurrentGenerationCount ;
2016-06-15 16:44:30 +00:00
return ( needToVisitNode | | cachedResults = = NULL ) ;
}
2016-07-20 15:46:00 +00:00
void CSSNodeCalculateLayout ( CSSNode * node , float availableWidth , float availableHeight , CSSDirection parentDirection ) {
2016-06-15 16:44:30 +00:00
// Increment the generation count. This will force the recursive routine to visit
// all dirty nodes at least once. Subsequent visits will be skipped if the input
// parameters don't change.
gCurrentGenerationCount + + ;
2016-07-20 13:40:26 +00:00
CSSMeasureMode widthMeasureMode = CSSMeasureModeUndefined ;
CSSMeasureMode heightMeasureMode = CSSMeasureModeUndefined ;
2016-07-08 10:46:41 +00:00
if ( ! isUndefined ( availableWidth ) ) {
2016-07-20 13:40:26 +00:00
widthMeasureMode = CSSMeasureModeExactly ;
} else if ( isStyleDimDefined ( node , CSSFlexDirectionRow ) ) {
availableWidth = node - > style . dimensions [ dim [ CSSFlexDirectionRow ] ] + getMarginAxis ( node , CSSFlexDirectionRow ) ;
widthMeasureMode = CSSMeasureModeExactly ;
} else if ( node - > style . maxDimensions [ CSSDimensionWidth ] > = 0.0 ) {
availableWidth = node - > style . maxDimensions [ CSSDimensionWidth ] ;
widthMeasureMode = CSSMeasureModeAtMost ;
2016-06-15 16:44:30 +00:00
}
2016-07-08 10:46:41 +00:00
if ( ! isUndefined ( availableHeight ) ) {
2016-07-20 13:40:26 +00:00
heightMeasureMode = CSSMeasureModeExactly ;
} else if ( isStyleDimDefined ( node , CSSFlexDirectionColumn ) ) {
availableHeight = node - > style . dimensions [ dim [ CSSFlexDirectionColumn ] ] + getMarginAxis ( node , CSSFlexDirectionColumn ) ;
heightMeasureMode = CSSMeasureModeExactly ;
} else if ( node - > style . maxDimensions [ CSSDimensionHeight ] > = 0.0 ) {
availableHeight = node - > style . maxDimensions [ CSSDimensionHeight ] ;
heightMeasureMode = CSSMeasureModeAtMost ;
2016-07-08 10:46:41 +00:00
}
2016-06-15 16:44:30 +00:00
if ( layoutNodeInternal ( node , availableWidth , availableHeight , parentDirection , widthMeasureMode , heightMeasureMode , true , " initial " ) ) {
setPosition ( node , node - > layout . direction ) ;
if ( gPrintTree ) {
2016-07-20 13:40:26 +00:00
CSSNodePrint ( node , CSSPrintOptionsLayout | CSSPrintOptionsChildren | CSSPrintOptionsStyle ) ;
2016-06-15 16:44:30 +00:00
}
}
}