yoga 0.0.0.1 → 0.0.0.2
raw patch · 16 files changed
+4813/−4193 lines, 16 filesdep ~bindings-DSLPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: bindings-DSL
API changes (from Hackage documentation)
+ Bindings.Yoga: c'YGNodeClone :: Ptr C'YGNode -> IO (Ptr C'YGNode)
+ Bindings.Yoga: c'YGNodeGetChildCount :: Ptr C'YGNode -> IO (CUInt)
+ Bindings.Yoga: c'YGNodeGetParent :: Ptr C'YGNode -> IO (Ptr C'YGNode)
+ Bindings.Yoga: c'YGNodeRemoveAllChildren :: Ptr C'YGNode -> IO ()
+ Bindings.Yoga: p'YGNodeClone :: FunPtr (Ptr C'YGNode -> IO (Ptr C'YGNode))
+ Bindings.Yoga: p'YGNodeGetChildCount :: FunPtr (Ptr C'YGNode -> IO (CUInt))
+ Bindings.Yoga: p'YGNodeGetParent :: FunPtr (Ptr C'YGNode -> IO (Ptr C'YGNode))
+ Bindings.Yoga: p'YGNodeRemoveAllChildren :: FunPtr (Ptr C'YGNode -> IO ())
+ Yoga: withDimensions :: Float -> Float -> (a -> Layout b) -> a -> Layout b
- Yoga: growable :: Float -> Size -> Size -> a -> Layout a
+ Yoga: growable :: Float -> Size -> Size -> (b -> Layout a) -> b -> Layout a
- Yoga: shrinkable :: Float -> Size -> Size -> a -> Layout a
+ Yoga: shrinkable :: Float -> Size -> Size -> (b -> Layout a) -> b -> Layout a
Files
- lib/Bindings/Yoga.hsc +4/−3
- lib/Bindings/Yoga/Enums.hsc +0/−1
- lib/Yoga.hs +78/−99
- yoga.cabal +9/−7
- yoga/YGEnums.cpp +228/−0
- yoga/YGEnums.h +157/−135
- yoga/YGMacros.h +47/−61
- yoga/YGMemFuncs.h +0/−16
- yoga/YGNodeList.c +0/−104
- yoga/YGNodeList.h +0/−33
- yoga/YGNodePrint.cpp +220/−0
- yoga/YGNodePrint.h +24/−0
- yoga/Yoga-internal.h +230/−0
- yoga/Yoga.c +0/−3487
- yoga/Yoga.cpp +3533/−0
- yoga/Yoga.h +283/−247
lib/Bindings/Yoga.hsc view
@@ -1,9 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE StandaloneDeriving #-}--{-# OPTIONS_HADDOCK hide #-}- -------------------------------------------------------------------------------- #include <Yoga.h>@@ -50,6 +47,7 @@ -} #ccall YGNodeNew, IO (Ptr <YGNode>)+#ccall YGNodeClone, Ptr <YGNode> -> IO (Ptr <YGNode>) #ccall YGNodeFree, Ptr <YGNode> -> IO () #ccall YGNodeFreeRecursive, Ptr <YGNode> -> IO () #ccall YGNodeReset, Ptr <YGNode> -> IO ()@@ -57,7 +55,10 @@ #ccall YGNodeInsertChild, Ptr <YGNode> -> Ptr <YGNode> -> CUInt -> IO () #ccall YGNodeRemoveChild, Ptr <YGNode> -> Ptr <YGNode> -> IO ()+#ccall YGNodeRemoveAllChildren, Ptr <YGNode> -> IO () #ccall YGNodeGetChild, Ptr <YGNode> -> CUInt -> IO (Ptr <YGNode>)+#ccall YGNodeGetParent, Ptr <YGNode> -> IO (Ptr <YGNode>)+#ccall YGNodeGetChildCount, Ptr <YGNode> -> IO (CUInt) #ccall YGNodeCalculateLayout, Ptr <YGNode> -> CFloat -> CFloat -> CInt -> IO ()
lib/Bindings/Yoga/Enums.hsc view
@@ -88,7 +88,6 @@ #num YGOverflowCount -- typedef enum YGExperimentalFeature {-#num YGExperimentalFeatureRounding #num YGExperimentalFeatureWebFlexBasis #num YGExperimentalFeatureCount
lib/Yoga.hs view
@@ -35,7 +35,7 @@ -- ** Leaf nodes Size(..),- shrinkable, growable, exact,+ shrinkable, growable, exact, withDimensions, -- ** Attributes Edge(..),@@ -80,16 +80,13 @@ _children :: [Layout a], _rootPtr :: ForeignPtr C'YGNode } | Container { _payload :: a,- _children :: [Layout a],- _childPtr :: Ptr C'YGNode }- | Leaf { _payload :: a,- _childPtr :: Ptr C'YGNode }+ _children :: [Layout a] }+ | Leaf { _payload :: a } deriving (Show, Eq, Ord) withNativePtr :: Layout a -> (Ptr C'YGNode -> IO b) -> IO b withNativePtr (Root _ _ ptr) f = withForeignPtr ptr f-withNativePtr (Container _ _ ptr) f = f ptr-withNativePtr (Leaf _ ptr) f = f ptr+withNativePtr _ _ = error "Internal: Only root nodes have pointers" -- | Children are a list of layouts annotated with a style for how they should -- be laid out in their container.@@ -104,34 +101,34 @@ instance Functor Layout where fmap f (Root x cs ptr) = Root (f x) (fmap (fmap f) cs) ptr- fmap f (Container x cs ptr) = Container (f x) (fmap (fmap f) cs) ptr- fmap f (Leaf x ptr) = Leaf (f x) ptr+ fmap f (Container x cs) = Container (f x) (fmap (fmap f) cs)+ fmap f (Leaf x) = Leaf (f x) instance Foldable Layout where foldMap f (Root x cs _) = f x `mappend` (foldMap (foldMap f) cs)- foldMap f (Container x cs _) = f x `mappend` (foldMap (foldMap f) cs)- foldMap f (Leaf x _) = f x+ foldMap f (Container x cs) = f x `mappend` (foldMap (foldMap f) cs)+ foldMap f (Leaf x) = f x foldl f z (Root x cs _) = foldl (foldl f) (f z x) cs- foldl f z (Container x cs _) = foldl (foldl f) (f z x) cs- foldl f z (Leaf x _) = f z x+ foldl f z (Container x cs) = foldl (foldl f) (f z x) cs+ foldl f z (Leaf x) = f z x foldr f z (Root x cs _) = f x $ foldr (flip $ foldr f) z cs- foldr f z (Container x cs _) = f x $ foldr (flip $ foldr f) z cs- foldr f z (Leaf x _) = f x z+ foldr f z (Container x cs) = f x $ foldr (flip $ foldr f) z cs+ foldr f z (Leaf x) = f x z instance Traversable Layout where traverse f (Root x cs ptr) = Root <$> f x <*> (sequenceA $ traverse f <$> cs) <*> pure ptr- traverse f (Container x cs ptr) =- Container <$> f x <*> (sequenceA $ traverse f <$> cs) <*> pure ptr- traverse f (Leaf x ptr) = Leaf <$> f x <*> pure ptr+ traverse f (Container x cs) =+ Container <$> f x <*> (sequenceA $ traverse f <$> cs)+ traverse f (Leaf x) = Leaf <$> f x sequenceA (Root x cs ptr) = Root <$> x <*> sequenceA (sequenceA <$> cs) <*> pure ptr- sequenceA (Container x cs ptr) =- Container <$> x <*> sequenceA (sequenceA <$> cs) <*> pure ptr- sequenceA (Leaf x ptr) = Leaf <$> x <*> pure ptr+ sequenceA (Container x cs) =+ Container <$> x <*> sequenceA (sequenceA <$> cs)+ sequenceA (Leaf x) = Leaf <$> x mkNode :: a -> IO (Layout a) mkNode x = do@@ -174,28 +171,13 @@ c'YGNodeStyleSetJustifyContent ptr just c'YGNodeStyleSetFlexWrap ptr c'YGWrapNoWrap - cs' <- flip mapM (zip cs [0..]) $ \(child, idx) ->- case child of- (Root p [] fptr) -> withForeignPtr fptr $ \oldptr -> do- newptr <- c'YGNodeNew- c'YGNodeCopyStyle newptr oldptr- c'YGNodeInsertChild ptr newptr idx- return $ Leaf p newptr- (Root p childs fptr) -> withForeignPtr fptr $ \oldptr -> do- newptr <- c'YGNodeNew- forM_ (zip childs [0..]) $ \(oldChild, oldChildIdx) -> do- case oldChild of- (Container _ _ oldChildPtr) -> do- c'YGNodeRemoveChild oldptr oldChildPtr- c'YGNodeInsertChild newptr oldChildPtr oldChildIdx- (Leaf _ oldChildPtr) -> do- c'YGNodeRemoveChild oldptr oldChildPtr- c'YGNodeInsertChild newptr oldChildPtr oldChildIdx- _ -> error "Removing native tree structure of root children!"- c'YGNodeCopyStyle newptr oldptr- c'YGNodeInsertChild ptr newptr idx- return $ Container p childs newptr- _ -> error "Adding non-root children to container!"+ cs' <- flip mapM (zip cs [0..]) $ \((Root p children fptr), idx) -> do+ withForeignPtr fptr $ \oldptr -> do+ newptr <- c'YGNodeClone oldptr+ c'YGNodeInsertChild ptr newptr idx+ return $ if null children+ then Leaf p+ else Container p children Root x cs' <$> newForeignPtr p'YGNodeFreeRecursive ptr assembleChildren :: Children a -> a -> IO (Layout a)@@ -306,23 +288,23 @@ c'YGNodeStyleSetMinHeight ptr $ realToFrac minHeight c'YGNodeStyleSetMaxHeight ptr $ realToFrac maxHeight --- | Creates a layout that may shrink up to the given size. The weight parameter+-- | Specifies layout may shrink up to the given size. The weight parameter -- is used to determine how much this layout will shrink in relation to any -- siblings.-shrinkable :: Float -> Size -> Size -> a -> Layout a-shrinkable weight width height x = unsafePerformIO $ do- n <- mkNode x+shrinkable :: Float -> Size -> Size -> (b -> Layout a) -> b -> Layout a+shrinkable weight width height mkNodeFn x = unsafePerformIO $ do+ let n = mkNodeFn x setWidth width n setHeight height n withNativePtr n $ \ptr -> c'YGNodeStyleSetFlexShrink ptr $ realToFrac weight return n --- | Creates a layout that may grow up to the given size. The weight parameter+-- | Specifies layout may grow up to the given size. The weight parameter -- is used to determine how much this layout will grow in relation to any -- siblings.-growable :: Float -> Size -> Size -> a -> Layout a-growable weight width height x = unsafePerformIO $ do- n <- mkNode x+growable :: Float -> Size -> Size -> (b -> Layout a) -> b -> Layout a+growable weight width height mkNodeFn x = unsafePerformIO $ do+ let n = mkNodeFn x setWidth width n setHeight height n withNativePtr n $ \ptr -> c'YGNodeStyleSetFlexGrow ptr $ realToFrac weight@@ -336,6 +318,15 @@ setHeight (Exact height) n return n +-- | Specifies the exact dimensions expected for a layout. Can be used for+-- containers and such when there is not necessarily any rendering involved. +withDimensions :: Float -> Float -> (a -> Layout b) -> a -> Layout b+withDimensions width height mkNodeFn x = unsafePerformIO $ do+ let n = mkNodeFn x+ setWidth (Exact width) n+ setHeight (Exact height) n+ return n+ -- | Allows a container to stretch to fit its parent stretched :: (b -> Layout a) -> b -> Layout a stretched mkNodeFn x =@@ -462,55 +453,31 @@ height <- realToFrac <$> c'YGNodeLayoutGetHeight ptr return $ LayoutInfo top left width height -renderTree :: (Functor m, Applicative m, Monad m) =>- LayoutInfo -> Layout a -> RenderFn m a b -> m (Layout b)-renderTree parentInfo (Leaf x ptr) f = do- info <- return $ unsafePerformIO $ layoutInfo ptr- Leaf <$> f (layoutWithParent parentInfo info) x <*> pure ptr-renderTree parentInfo (Container x cs ptr) f = do+renderNodeWithChildren :: (Functor m, Applicative m, Monad m, Monoid b) =>+ LayoutInfo -> a -> [Layout a] -> Ptr C'YGNode+ -> RenderFn m a (b, c)+ -> m (b, c, [Layout c])+renderNodeWithChildren parentInfo x children ptr f = do info <- return $ unsafePerformIO $ layoutInfo ptr let thisInfo = layoutWithParent parentInfo info- Container- <$> f thisInfo x- <*> mapM (flip (renderTree thisInfo) f) cs- <*> pure ptr-renderTree parentInfo (Root x cs ptr) f = do- info <- return $ unsafePerformIO $ withForeignPtr ptr layoutInfo- let thisInfo = layoutWithParent parentInfo info- Root- <$> f thisInfo x- <*> mapM (flip (renderTree thisInfo) f) cs- <*> pure ptr---- | Renders a layout with the user-supplied function. The renderer traverses--- the tree from root node to children and transforms each payload using the--- user-supplied function.-render :: (Functor m, Applicative m, Monad m) =>- Layout a -> RenderFn m a b -> m (Layout b)-render lyt f = do- _ <- (unsafePerformIO $ withNativePtr lyt calculateLayout) `seq` return ()- renderTree emptyInfo lyt f+ (m, y) <- f thisInfo x+ cs <- flip mapM (zip children [0..]) $ \(child, childIdx) -> do+ childPtr <- return $ unsafePerformIO $ c'YGNodeGetChild ptr childIdx+ foldRenderTree thisInfo child childPtr f+ return (mappend m . foldr mappend mempty . map fst $ cs, y, map snd cs) foldRenderTree :: (Functor m, Applicative m, Monad m, Monoid b) =>- LayoutInfo -> Layout a -> RenderFn m a (b, c) -> m (b, Layout c)-foldRenderTree parentInfo (Leaf x ptr) f = do- info <- return $ unsafePerformIO $ layoutInfo ptr- (m, y) <- f (layoutWithParent parentInfo info) x- return (mappend m mempty, Leaf y ptr)-foldRenderTree parentInfo (Container x cs ptr) f = do- info <- return $ unsafePerformIO $ layoutInfo ptr- let thisInfo = layoutWithParent parentInfo info- (m, y) <- f thisInfo x- cs' <- mapM (flip (foldRenderTree thisInfo) f) cs- return (mappend m . foldr mappend mempty . map fst $ cs',- Container y (map snd cs') ptr)-foldRenderTree parentInfo (Root x cs ptr) f = do- info <- return $ unsafePerformIO $ withForeignPtr ptr layoutInfo- let thisInfo = layoutWithParent parentInfo info- (m, y) <- f thisInfo x- cs' <- mapM (flip (foldRenderTree thisInfo) f) cs- return (mappend m . foldr mappend mempty . map fst $ cs',- Root y (map snd cs') ptr)+ LayoutInfo -> Layout a -> Ptr C'YGNode -> RenderFn m a (b, c) ->+ m (b, Layout c)+foldRenderTree parentInfo (Root x children fptr) ptr f = do+ (result, y, cs) <- renderNodeWithChildren parentInfo x children ptr f+ return $ (result, Root y cs fptr)+foldRenderTree parentInfo (Container x children) ptr f = do+ (result, y, cs) <- renderNodeWithChildren parentInfo x children ptr f+ return $ (result, Container y cs)+foldRenderTree parentInfo (Leaf x) ptr f = do+ (result, y, []) <- renderNodeWithChildren parentInfo x [] ptr f+ return $ (result, Leaf y) -- | Renders a layout with the user-supplied function. For each return value -- of type '(b, c)', we append the first result to the output of the previous@@ -519,6 +486,18 @@ -- a new layout with payloads of type 'c'. foldRender :: (Functor m, Applicative m, Monad m, Monoid b) => Layout a -> RenderFn m a (b, c) -> m (b, Layout c)-foldRender lyt f = do- _ <- (unsafePerformIO $ withNativePtr lyt calculateLayout) `seq` return ()- foldRenderTree emptyInfo lyt f+foldRender lyt@(Root _ _ fptr) f = do+ rootPtr <- return $ unsafePerformIO $ withForeignPtr fptr $ \ptr -> do+ calculateLayout ptr+ return ptr+ foldRenderTree emptyInfo lyt rootPtr f+foldRender _ _ = error "Internal: Rendering must be done from the root node"++-- | Renders a layout with the user-supplied function. The renderer traverses+-- the tree from root node to children and transforms each payload using the+-- user-supplied function.+render :: (Functor m, Applicative m, Monad m) =>+ Layout a -> RenderFn m a b -> m (Layout b)+render lyt f =+ let f' lytInfo x = ((,) ()) <$> f lytInfo x+ in snd <$> foldRender lyt f'
yoga.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/ name: yoga-version: 0.0.0.1+version: 0.0.0.2 synopsis: Bindings to Facebook's Yoga layout library description: The <https://facebook.github.com/yoga Yoga> library from <https://facebook.github.com/ Facebook> is a fast layout engine@@ -47,13 +47,15 @@ include-dirs: yoga/ install-includes: YGEnums.h YGMacros.h- YGMemFuncs.h- YGNodeList.h+ YGNodePrint.h Yoga.h- c-sources: yoga/YGNodeList.c- yoga/Yoga.c+ Yoga-internal.h+ c-sources: yoga/YGNodePrint.cpp+ yoga/Yoga.cpp+ yoga/YGEnums.cpp - cc-options: -Wno-format -std=gnu99+ cc-options: -Dnullptr=NULL+ extra-libraries: stdc++ exposed-modules: Bindings.Yoga@@ -65,7 +67,7 @@ build-depends: base >= 4 && < 6, ieee754 >= 0.7,- bindings-DSL == 1.0.*+ bindings-DSL flag examples description: Build examples
+ yoga/YGEnums.cpp view
@@ -0,0 +1,228 @@+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "YGEnums.h" + +const char *YGAlignToString(const YGAlign value){ + switch(value){ + case YGAlignAuto: + return "auto"; + case YGAlignFlexStart: + return "flex-start"; + case YGAlignCenter: + return "center"; + case YGAlignFlexEnd: + return "flex-end"; + case YGAlignStretch: + return "stretch"; + case YGAlignBaseline: + return "baseline"; + case YGAlignSpaceBetween: + return "space-between"; + case YGAlignSpaceAround: + return "space-around"; + } + return "unknown"; +} + +const char *YGDimensionToString(const YGDimension value){ + switch(value){ + case YGDimensionWidth: + return "width"; + case YGDimensionHeight: + return "height"; + } + return "unknown"; +} + +const char *YGDirectionToString(const YGDirection value){ + switch(value){ + case YGDirectionInherit: + return "inherit"; + case YGDirectionLTR: + return "ltr"; + case YGDirectionRTL: + return "rtl"; + } + return "unknown"; +} + +const char *YGDisplayToString(const YGDisplay value){ + switch(value){ + case YGDisplayFlex: + return "flex"; + case YGDisplayNone: + return "none"; + } + return "unknown"; +} + +const char *YGEdgeToString(const YGEdge value){ + switch(value){ + case YGEdgeLeft: + return "left"; + case YGEdgeTop: + return "top"; + case YGEdgeRight: + return "right"; + case YGEdgeBottom: + return "bottom"; + case YGEdgeStart: + return "start"; + case YGEdgeEnd: + return "end"; + case YGEdgeHorizontal: + return "horizontal"; + case YGEdgeVertical: + return "vertical"; + case YGEdgeAll: + return "all"; + } + return "unknown"; +} + +const char *YGExperimentalFeatureToString(const YGExperimentalFeature value){ + switch(value){ + case YGExperimentalFeatureWebFlexBasis: + return "web-flex-basis"; + } + return "unknown"; +} + +const char *YGFlexDirectionToString(const YGFlexDirection value){ + switch(value){ + case YGFlexDirectionColumn: + return "column"; + case YGFlexDirectionColumnReverse: + return "column-reverse"; + case YGFlexDirectionRow: + return "row"; + case YGFlexDirectionRowReverse: + return "row-reverse"; + } + return "unknown"; +} + +const char *YGJustifyToString(const YGJustify value){ + switch(value){ + case YGJustifyFlexStart: + return "flex-start"; + case YGJustifyCenter: + return "center"; + case YGJustifyFlexEnd: + return "flex-end"; + case YGJustifySpaceBetween: + return "space-between"; + case YGJustifySpaceAround: + return "space-around"; + case YGJustifySpaceEvenly: + return "space-evenly"; + } + return "unknown"; +} + +const char *YGLogLevelToString(const YGLogLevel value){ + switch(value){ + case YGLogLevelError: + return "error"; + case YGLogLevelWarn: + return "warn"; + case YGLogLevelInfo: + return "info"; + case YGLogLevelDebug: + return "debug"; + case YGLogLevelVerbose: + return "verbose"; + case YGLogLevelFatal: + return "fatal"; + } + return "unknown"; +} + +const char *YGMeasureModeToString(const YGMeasureMode value){ + switch(value){ + case YGMeasureModeUndefined: + return "undefined"; + case YGMeasureModeExactly: + return "exactly"; + case YGMeasureModeAtMost: + return "at-most"; + } + return "unknown"; +} + +const char *YGNodeTypeToString(const YGNodeType value){ + switch(value){ + case YGNodeTypeDefault: + return "default"; + case YGNodeTypeText: + return "text"; + } + return "unknown"; +} + +const char *YGOverflowToString(const YGOverflow value){ + switch(value){ + case YGOverflowVisible: + return "visible"; + case YGOverflowHidden: + return "hidden"; + case YGOverflowScroll: + return "scroll"; + } + return "unknown"; +} + +const char *YGPositionTypeToString(const YGPositionType value){ + switch(value){ + case YGPositionTypeRelative: + return "relative"; + case YGPositionTypeAbsolute: + return "absolute"; + } + return "unknown"; +} + +const char *YGPrintOptionsToString(const YGPrintOptions value){ + switch(value){ + case YGPrintOptionsLayout: + return "layout"; + case YGPrintOptionsStyle: + return "style"; + case YGPrintOptionsChildren: + return "children"; + } + return "unknown"; +} + +const char *YGUnitToString(const YGUnit value){ + switch(value){ + case YGUnitUndefined: + return "undefined"; + case YGUnitPoint: + return "point"; + case YGUnitPercent: + return "percent"; + case YGUnitAuto: + return "auto"; + } + return "unknown"; +} + +const char *YGWrapToString(const YGWrap value){ + switch(value){ + case YGWrapNoWrap: + return "no-wrap"; + case YGWrapWrap: + return "wrap"; + case YGWrapWrapReverse: + return "wrap-reverse"; + } + return "unknown"; +}
yoga/YGEnums.h view
@@ -1,135 +1,157 @@-/**- * Copyright (c) 2014-present, Facebook, Inc.- * All rights reserved.- *- * This source code is licensed under the BSD-style license found in the- * LICENSE file in the root directory of this source tree. An additional grant- * of patent rights can be found in the PATENTS file in the same directory.- */--#pragma once--#include "YGMacros.h"--YG_EXTERN_C_BEGIN--#define YGAlignCount 8-typedef YG_ENUM_BEGIN(YGAlign) {- YGAlignAuto,- YGAlignFlexStart,- YGAlignCenter,- YGAlignFlexEnd,- YGAlignStretch,- YGAlignBaseline,- YGAlignSpaceBetween,- YGAlignSpaceAround,-} YG_ENUM_END(YGAlign);--#define YGDimensionCount 2-typedef YG_ENUM_BEGIN(YGDimension) {- YGDimensionWidth,- YGDimensionHeight,-} YG_ENUM_END(YGDimension);--#define YGDirectionCount 3-typedef YG_ENUM_BEGIN(YGDirection) {- YGDirectionInherit,- YGDirectionLTR,- YGDirectionRTL,-} YG_ENUM_END(YGDirection);--#define YGDisplayCount 2-typedef YG_ENUM_BEGIN(YGDisplay) {- YGDisplayFlex,- YGDisplayNone,-} YG_ENUM_END(YGDisplay);--#define YGEdgeCount 9-typedef YG_ENUM_BEGIN(YGEdge) {- YGEdgeLeft,- YGEdgeTop,- YGEdgeRight,- YGEdgeBottom,- YGEdgeStart,- YGEdgeEnd,- YGEdgeHorizontal,- YGEdgeVertical,- YGEdgeAll,-} YG_ENUM_END(YGEdge);--#define YGExperimentalFeatureCount 3-typedef YG_ENUM_BEGIN(YGExperimentalFeature) {- YGExperimentalFeatureRounding,- YGExperimentalFeatureWebFlexBasis,- YGExperimentalFeatureMinFlexFix,-} YG_ENUM_END(YGExperimentalFeature);--#define YGFlexDirectionCount 4-typedef YG_ENUM_BEGIN(YGFlexDirection) {- YGFlexDirectionColumn,- YGFlexDirectionColumnReverse,- YGFlexDirectionRow,- YGFlexDirectionRowReverse,-} YG_ENUM_END(YGFlexDirection);--#define YGJustifyCount 5-typedef YG_ENUM_BEGIN(YGJustify) {- YGJustifyFlexStart,- YGJustifyCenter,- YGJustifyFlexEnd,- YGJustifySpaceBetween,- YGJustifySpaceAround,-} YG_ENUM_END(YGJustify);--#define YGLogLevelCount 5-typedef YG_ENUM_BEGIN(YGLogLevel) {- YGLogLevelError,- YGLogLevelWarn,- YGLogLevelInfo,- YGLogLevelDebug,- YGLogLevelVerbose,-} YG_ENUM_END(YGLogLevel);--#define YGMeasureModeCount 3-typedef YG_ENUM_BEGIN(YGMeasureMode) {- YGMeasureModeUndefined,- YGMeasureModeExactly,- YGMeasureModeAtMost,-} YG_ENUM_END(YGMeasureMode);--#define YGOverflowCount 3-typedef YG_ENUM_BEGIN(YGOverflow) {- YGOverflowVisible,- YGOverflowHidden,- YGOverflowScroll,-} YG_ENUM_END(YGOverflow);--#define YGPositionTypeCount 2-typedef YG_ENUM_BEGIN(YGPositionType) {- YGPositionTypeRelative,- YGPositionTypeAbsolute,-} YG_ENUM_END(YGPositionType);--#define YGPrintOptionsCount 3-typedef YG_ENUM_BEGIN(YGPrintOptions) {- YGPrintOptionsLayout = 1,- YGPrintOptionsStyle = 2,- YGPrintOptionsChildren = 4,-} YG_ENUM_END(YGPrintOptions);--#define YGUnitCount 4-typedef YG_ENUM_BEGIN(YGUnit) {- YGUnitUndefined,- YGUnitPoint,- YGUnitPercent,- YGUnitAuto,-} YG_ENUM_END(YGUnit);--#define YGWrapCount 3-typedef YG_ENUM_BEGIN(YGWrap) {- YGWrapNoWrap,- YGWrapWrap,- YGWrapWrapReverse,-} YG_ENUM_END(YGWrap);--YG_EXTERN_C_END+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#include "YGMacros.h" + +YG_EXTERN_C_BEGIN + +#define YGAlignCount 8 +typedef YG_ENUM_BEGIN(YGAlign) { + YGAlignAuto, + YGAlignFlexStart, + YGAlignCenter, + YGAlignFlexEnd, + YGAlignStretch, + YGAlignBaseline, + YGAlignSpaceBetween, + YGAlignSpaceAround, +} YG_ENUM_END(YGAlign); +WIN_EXPORT const char *YGAlignToString(const YGAlign value); + +#define YGDimensionCount 2 +typedef YG_ENUM_BEGIN(YGDimension) { + YGDimensionWidth, + YGDimensionHeight, +} YG_ENUM_END(YGDimension); +WIN_EXPORT const char *YGDimensionToString(const YGDimension value); + +#define YGDirectionCount 3 +typedef YG_ENUM_BEGIN(YGDirection) { + YGDirectionInherit, + YGDirectionLTR, + YGDirectionRTL, +} YG_ENUM_END(YGDirection); +WIN_EXPORT const char *YGDirectionToString(const YGDirection value); + +#define YGDisplayCount 2 +typedef YG_ENUM_BEGIN(YGDisplay) { + YGDisplayFlex, + YGDisplayNone, +} YG_ENUM_END(YGDisplay); +WIN_EXPORT const char *YGDisplayToString(const YGDisplay value); + +#define YGEdgeCount 9 +typedef YG_ENUM_BEGIN(YGEdge) { + YGEdgeLeft, + YGEdgeTop, + YGEdgeRight, + YGEdgeBottom, + YGEdgeStart, + YGEdgeEnd, + YGEdgeHorizontal, + YGEdgeVertical, + YGEdgeAll, +} YG_ENUM_END(YGEdge); +WIN_EXPORT const char *YGEdgeToString(const YGEdge value); + +#define YGExperimentalFeatureCount 1 +typedef YG_ENUM_BEGIN(YGExperimentalFeature) { + YGExperimentalFeatureWebFlexBasis, +} YG_ENUM_END(YGExperimentalFeature); +WIN_EXPORT const char *YGExperimentalFeatureToString(const YGExperimentalFeature value); + +#define YGFlexDirectionCount 4 +typedef YG_ENUM_BEGIN(YGFlexDirection) { + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse, +} YG_ENUM_END(YGFlexDirection); +WIN_EXPORT const char *YGFlexDirectionToString(const YGFlexDirection value); + +#define YGJustifyCount 6 +typedef YG_ENUM_BEGIN(YGJustify){ + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, + YGJustifySpaceEvenly, +} YG_ENUM_END(YGJustify); +WIN_EXPORT const char *YGJustifyToString(const YGJustify value); + +#define YGLogLevelCount 6 +typedef YG_ENUM_BEGIN(YGLogLevel) { + YGLogLevelError, + YGLogLevelWarn, + YGLogLevelInfo, + YGLogLevelDebug, + YGLogLevelVerbose, + YGLogLevelFatal, +} YG_ENUM_END(YGLogLevel); +WIN_EXPORT const char *YGLogLevelToString(const YGLogLevel value); + +#define YGMeasureModeCount 3 +typedef YG_ENUM_BEGIN(YGMeasureMode) { + YGMeasureModeUndefined, + YGMeasureModeExactly, + YGMeasureModeAtMost, +} YG_ENUM_END(YGMeasureMode); +WIN_EXPORT const char *YGMeasureModeToString(const YGMeasureMode value); + +#define YGNodeTypeCount 2 +typedef YG_ENUM_BEGIN(YGNodeType) { + YGNodeTypeDefault, + YGNodeTypeText, +} YG_ENUM_END(YGNodeType); +WIN_EXPORT const char *YGNodeTypeToString(const YGNodeType value); + +#define YGOverflowCount 3 +typedef YG_ENUM_BEGIN(YGOverflow) { + YGOverflowVisible, + YGOverflowHidden, + YGOverflowScroll, +} YG_ENUM_END(YGOverflow); +WIN_EXPORT const char *YGOverflowToString(const YGOverflow value); + +#define YGPositionTypeCount 2 +typedef YG_ENUM_BEGIN(YGPositionType) { + YGPositionTypeRelative, + YGPositionTypeAbsolute, +} YG_ENUM_END(YGPositionType); +WIN_EXPORT const char *YGPositionTypeToString(const YGPositionType value); + +#define YGPrintOptionsCount 3 +typedef YG_ENUM_BEGIN(YGPrintOptions) { + YGPrintOptionsLayout = 1, + YGPrintOptionsStyle = 2, + YGPrintOptionsChildren = 4, +} YG_ENUM_END(YGPrintOptions); +WIN_EXPORT const char *YGPrintOptionsToString(const YGPrintOptions value); + +#define YGUnitCount 4 +typedef YG_ENUM_BEGIN(YGUnit) { + YGUnitUndefined, + YGUnitPoint, + YGUnitPercent, + YGUnitAuto, +} YG_ENUM_END(YGUnit); +WIN_EXPORT const char *YGUnitToString(const YGUnit value); + +#define YGWrapCount 3 +typedef YG_ENUM_BEGIN(YGWrap) { + YGWrapNoWrap, + YGWrapWrap, + YGWrapWrapReverse, +} YG_ENUM_END(YGWrap); +WIN_EXPORT const char *YGWrapToString(const YGWrap value); + +YG_EXTERN_C_END
yoga/YGMacros.h view
@@ -1,61 +1,47 @@-/**- * Copyright (c) 2014-present, Facebook, Inc.- * All rights reserved.- *- * This source code is licensed under the BSD-style license found in the- * LICENSE file in the root directory of this source tree. An additional grant- * of patent rights can be found in the PATENTS file in the same directory.- */--#pragma once--#ifdef __cplusplus-#define YG_EXTERN_C_BEGIN extern "C" {-#define YG_EXTERN_C_END }-#else-#define YG_EXTERN_C_BEGIN-#define YG_EXTERN_C_END-#endif--#ifdef _WINDLL-#define WIN_EXPORT __declspec(dllexport)-#else-#define WIN_EXPORT-#endif--#ifdef WINARMDLL-#define WIN_STRUCT(type) type*-#define WIN_STRUCT_REF(value) &value-#else-#define WIN_STRUCT(type) type-#define WIN_STRUCT_REF(value) value-#endif--#ifndef FB_ASSERTIONS_ENABLED-#define FB_ASSERTIONS_ENABLED 1-#endif--#if FB_ASSERTIONS_ENABLED-#define YG_ABORT() abort()-#else-#define YG_ABORT()-#endif--#ifndef YG_ASSERT-#define YG_ASSERT(X, message) \- if (!(X)) { \- YGLog(YGLogLevelError, "%s", message); \- YG_ABORT(); \- }-#endif--#ifdef NS_ENUM-// Cannot use NSInteger as NSInteger has a different size than int (which is the default type of a-// enum).-// Therefor when linking the Yoga C library into obj-c the header is a missmatch for the Yoga ABI.-#define YG_ENUM_BEGIN(name) NS_ENUM(int, name)-#define YG_ENUM_END(name)-#else-#define YG_ENUM_BEGIN(name) enum name-#define YG_ENUM_END(name) name-#endif+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#ifdef __cplusplus +#define YG_EXTERN_C_BEGIN extern "C" { +#define YG_EXTERN_C_END } +#else +#define YG_EXTERN_C_BEGIN +#define YG_EXTERN_C_END +#endif + +#ifdef _WINDLL +#define WIN_EXPORT __declspec(dllexport) +#else +#define WIN_EXPORT +#endif + +#ifdef WINARMDLL +#define WIN_STRUCT(type) type * +#define WIN_STRUCT_REF(value) &value +#else +#define WIN_STRUCT(type) type +#define WIN_STRUCT_REF(value) value +#endif + +#ifndef FB_ASSERTIONS_ENABLED +#define FB_ASSERTIONS_ENABLED 1 +#endif + +#ifdef NS_ENUM +// Cannot use NSInteger as NSInteger has a different size than int (which is the default type of a +// enum). +// Therefor when linking the Yoga C library into obj-c the header is a missmatch for the Yoga ABI. +#define YG_ENUM_BEGIN(name) NS_ENUM(int, name) +#define YG_ENUM_END(name) +#else +#define YG_ENUM_BEGIN(name) enum name +#define YG_ENUM_END(name) name +#endif
− yoga/YGMemFuncs.h
@@ -1,16 +0,0 @@-#pragma once-#include "YGMacros.h"--YG_EXTERN_C_BEGIN--typedef void *(*YGMalloc)(size_t size);-typedef void *(*YGCalloc)(size_t count, size_t size);-typedef void *(*YGRealloc)(void *ptr, size_t size);-typedef void (*YGFree)(void *ptr);--WIN_EXPORT YGMalloc YGGetMalloc();-WIN_EXPORT YGCalloc YGGetCalloc();-WIN_EXPORT YGRealloc YGGetRealloc();-WIN_EXPORT YGFree YGGetFree();--YG_EXTERN_C_END
− yoga/YGNodeList.c
@@ -1,104 +0,0 @@-/**- * Copyright (c) 2014-present, Facebook, Inc.- * All rights reserved.- *- * This source code is licensed under the BSD-style license found in the- * LICENSE file in the root directory of this source tree. An additional grant- * of patent rights can be found in the PATENTS file in the same directory.- */--#include "YGNodeList.h"--extern YGMalloc gYGMalloc;-extern YGRealloc gYGRealloc;-extern YGFree gYGFree;--struct YGNodeList {- uint32_t capacity;- uint32_t count;- YGNodeRef *items;-};--YGNodeListRef YGNodeListNew(const uint32_t initialCapacity) {- const YGNodeListRef list = gYGMalloc(sizeof(struct YGNodeList));- YG_ASSERT(list != NULL, "Could not allocate memory for list");-- list->capacity = initialCapacity;- list->count = 0;- list->items = gYGMalloc(sizeof(YGNodeRef) * list->capacity);- YG_ASSERT(list->items != NULL, "Could not allocate memory for items");-- return list;-}--void YGNodeListFree(const YGNodeListRef list) {- if (list) {- gYGFree(list->items);- gYGFree(list);- }-}--uint32_t YGNodeListCount(const YGNodeListRef list) {- if (list) {- return list->count;- }- return 0;-}--void YGNodeListAdd(YGNodeListRef *listp, const YGNodeRef node) {- if (!*listp) {- *listp = YGNodeListNew(4);- }- YGNodeListInsert(listp, node, (*listp)->count);-}--void YGNodeListInsert(YGNodeListRef *listp, const YGNodeRef node, const uint32_t index) {- if (!*listp) {- *listp = YGNodeListNew(4);- }- YGNodeListRef list = *listp;-- if (list->count == list->capacity) {- list->capacity *= 2;- list->items = gYGRealloc(list->items, sizeof(YGNodeRef) * list->capacity);- YG_ASSERT(list->items != NULL, "Could not extend allocation for items");- }-- for (uint32_t i = list->count; i > index; i--) {- list->items[i] = list->items[i - 1];- }-- list->count++;- list->items[index] = node;-}--YGNodeRef YGNodeListRemove(const YGNodeListRef list, const uint32_t index) {- const YGNodeRef removed = list->items[index];- list->items[index] = NULL;-- for (uint32_t i = index; i < list->count - 1; i++) {- list->items[i] = list->items[i + 1];- list->items[i + 1] = NULL;- }-- list->count--;- return removed;-}--YGNodeRef YGNodeListDelete(const YGNodeListRef list, const YGNodeRef node) {- for (uint32_t i = 0; i < list->count; i++) {- if (list->items[i] == node) {- return YGNodeListRemove(list, i);- }- }-- return NULL;-}--YGNodeRef YGNodeListGet(const YGNodeListRef list, const uint32_t index) {- if (YGNodeListCount(list) > 0) {- return list->items[index];- }-- return NULL;-}
− yoga/YGNodeList.h
@@ -1,33 +0,0 @@-/**- * Copyright (c) 2014-present, Facebook, Inc.- * All rights reserved.- *- * This source code is licensed under the BSD-style license found in the- * LICENSE file in the root directory of this source tree. An additional grant- * of patent rights can be found in the PATENTS file in the same directory.- */--#pragma once--#include <assert.h>-#include <stdint.h>-#include <stdio.h>-#include <stdlib.h>--#include "YGMacros.h"-#include "Yoga.h"--YG_EXTERN_C_BEGIN--typedef struct YGNodeList *YGNodeListRef;--YGNodeListRef YGNodeListNew(const uint32_t initialCapacity);-void YGNodeListFree(const YGNodeListRef list);-uint32_t YGNodeListCount(const YGNodeListRef list);-void YGNodeListAdd(YGNodeListRef *listp, const YGNodeRef node);-void YGNodeListInsert(YGNodeListRef *listp, const YGNodeRef node, const uint32_t index);-YGNodeRef YGNodeListRemove(const YGNodeListRef list, const uint32_t index);-YGNodeRef YGNodeListDelete(const YGNodeListRef list, const YGNodeRef node);-YGNodeRef YGNodeListGet(const YGNodeListRef list, const uint32_t index);--YG_EXTERN_C_END
+ yoga/YGNodePrint.cpp view
@@ -0,0 +1,220 @@+/* + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "YGNodePrint.h" +#include <stdarg.h> +#include "YGEnums.h" +#include "Yoga-internal.h" + +namespace facebook { +namespace yoga { +typedef std::string string; + +static void indent(string* base, uint32_t level) { + for (uint32_t i = 0; i < level; ++i) { + base->append(" "); + } +} + +static bool areFourValuesEqual(const YGValue four[4]) { + return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) && + YGValueEqual(four[0], four[3]); +} + +static void appendFormatedString(string* str, const char* fmt, ...) { + char buffer[1024]; + va_list args; + va_start(args, fmt); + va_list argsCopy; + va_copy(argsCopy, args); + va_end(args); + vsnprintf(buffer, 1024, fmt, argsCopy); + va_end(argsCopy); + string result = string(buffer); + str->append(result); +} + +static void +appendFloatIfNotUndefined(string* base, const string key, const float num) { + if (!YGFloatIsUndefined(num)) { + appendFormatedString(base, "%s: %g; ", key.c_str(), num); + } +} + +static void appendNumberIfNotUndefined( + string* base, + const string key, + const YGValue* const number) { + if (number->unit != YGUnitUndefined) { + if (number->unit == YGUnitAuto) { + base->append(key + ": auto; "); + } else { + string unit = number->unit == YGUnitPoint ? "px" : "%%"; + appendFormatedString( + base, "%s: %g%s; ", key.c_str(), number->value, unit.c_str()); + } + } +} + +static void appendNumberIfNotAuto( + string* base, + const string key, + const YGValue* const number) { + if (number->unit != YGUnitAuto) { + appendNumberIfNotUndefined(base, key, number); + } +} + +static void appendNumberIfNotZero( + string* base, + const string str, + const YGValue* const number) { + if (!YGFloatsEqual(number->value, 0)) { + appendNumberIfNotUndefined(base, str, number); + } +} + +static void appendEdges(string* base, const string key, const YGValue* edges) { + if (areFourValuesEqual(edges)) { + appendNumberIfNotZero(base, key, &edges[YGEdgeLeft]); + } else { + for (int edge = YGEdgeLeft; edge != YGEdgeAll; ++edge) { + string str = key + "-" + YGEdgeToString(static_cast<YGEdge>(edge)); + appendNumberIfNotZero(base, str, &edges[edge]); + } + } +} + +static void appendEdgeIfNotUndefined( + string* base, + const string str, + const YGValue* edges, + const YGEdge edge) { + appendNumberIfNotUndefined( + base, str, YGComputedEdgeValue(edges, edge, &YGValueUndefined)); +} + +void YGNodeToString( + std::string* str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level) { + indent(str, level); + appendFormatedString(str, "<div "); + if (node->print != nullptr) { + node->print(node); + } + + if (options & YGPrintOptionsLayout) { + appendFormatedString(str, "layout=\""); + appendFormatedString( + str, "width: %g; ", node->layout.dimensions[YGDimensionWidth]); + appendFormatedString( + str, "height: %g; ", node->layout.dimensions[YGDimensionHeight]); + appendFormatedString(str, "top: %g; ", node->layout.position[YGEdgeTop]); + appendFormatedString(str, "left: %g;", node->layout.position[YGEdgeLeft]); + appendFormatedString(str, "\" "); + } + + if (options & YGPrintOptionsStyle) { + appendFormatedString(str, "style=\""); + if (node->style.flexDirection != gYGNodeDefaults.style.flexDirection) { + appendFormatedString( + str, + "flex-direction: %s; ", + YGFlexDirectionToString(node->style.flexDirection)); + } + if (node->style.justifyContent != gYGNodeDefaults.style.justifyContent) { + appendFormatedString( + str, + "justify-content: %s; ", + YGJustifyToString(node->style.justifyContent)); + } + if (node->style.alignItems != gYGNodeDefaults.style.alignItems) { + appendFormatedString( + str, "align-items: %s; ", YGAlignToString(node->style.alignItems)); + } + if (node->style.alignContent != gYGNodeDefaults.style.alignContent) { + appendFormatedString( + str, + "align-content: %s; ", + YGAlignToString(node->style.alignContent)); + } + if (node->style.alignSelf != gYGNodeDefaults.style.alignSelf) { + appendFormatedString( + str, "align-self: %s; ", YGAlignToString(node->style.alignSelf)); + } + appendFloatIfNotUndefined(str, "flex-grow", node->style.flexGrow); + appendFloatIfNotUndefined(str, "flex-shrink", node->style.flexShrink); + appendNumberIfNotAuto(str, "flex-basis", &node->style.flexBasis); + appendFloatIfNotUndefined(str, "flex", node->style.flex); + + if (node->style.flexWrap != gYGNodeDefaults.style.flexWrap) { + appendFormatedString( + str, "flexWrap: %s; ", YGWrapToString(node->style.flexWrap)); + } + + if (node->style.overflow != gYGNodeDefaults.style.overflow) { + appendFormatedString( + str, "overflow: %s; ", YGOverflowToString(node->style.overflow)); + } + + if (node->style.display != gYGNodeDefaults.style.display) { + appendFormatedString( + str, "display: %s; ", YGDisplayToString(node->style.display)); + } + appendEdges(str, "margin", node->style.margin); + appendEdges(str, "padding", node->style.padding); + appendEdges(str, "border", node->style.border); + + appendNumberIfNotAuto( + str, "width", &node->style.dimensions[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "height", &node->style.dimensions[YGDimensionHeight]); + appendNumberIfNotAuto( + str, "max-width", &node->style.maxDimensions[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "max-height", &node->style.maxDimensions[YGDimensionHeight]); + appendNumberIfNotAuto( + str, "min-width", &node->style.minDimensions[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "min-height", &node->style.minDimensions[YGDimensionHeight]); + + if (node->style.positionType != gYGNodeDefaults.style.positionType) { + appendFormatedString( + str, + "position: %s; ", + YGPositionTypeToString(node->style.positionType)); + } + + appendEdgeIfNotUndefined(str, "left", node->style.position, YGEdgeLeft); + appendEdgeIfNotUndefined(str, "right", node->style.position, YGEdgeRight); + appendEdgeIfNotUndefined(str, "top", node->style.position, YGEdgeTop); + appendEdgeIfNotUndefined(str, "bottom", node->style.position, YGEdgeBottom); + appendFormatedString(str, "\" "); + + if (node->measure != nullptr) { + appendFormatedString(str, "has-custom-measure=\"true\""); + } + } + appendFormatedString(str, ">"); + + const uint32_t childCount = node->children.size(); + if (options & YGPrintOptionsChildren && childCount > 0) { + for (uint32_t i = 0; i < childCount; i++) { + appendFormatedString(str, "\n"); + YGNodeToString(str, YGNodeGetChild(node, i), options, level + 1); + } + appendFormatedString(str, "\n"); + indent(str, level); + } + appendFormatedString(str, "</div>"); +} +} // namespace yoga +} // namespace facebook
+ yoga/YGNodePrint.h view
@@ -0,0 +1,24 @@+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once +#include <string> + +#include "Yoga.h" + +namespace facebook { +namespace yoga { + +void YGNodeToString( + std::string* str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level); + +} // namespace yoga +} // namespace facebook
+ yoga/Yoga-internal.h view
@@ -0,0 +1,230 @@+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once +#include <vector> + +#include "Yoga.h" + +typedef std::vector<YGNodeRef> YGVector; + +YG_EXTERN_C_BEGIN + +WIN_EXPORT float YGRoundValueToPixelGrid(const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor); + +YG_EXTERN_C_END + +typedef struct YGCachedMeasurement { + float availableWidth; + float availableHeight; + YGMeasureMode widthMeasureMode; + YGMeasureMode heightMeasureMode; + + float computedWidth; + float computedHeight; +} YGCachedMeasurement; + +// This value was chosen based on empiracle data. Even the most complicated +// layouts should not require more than 16 entries to fit within the cache. +#define YG_MAX_CACHED_RESULT_COUNT 16 + +typedef struct YGLayout { + float position[4]; + float dimensions[2]; + float margin[6]; + float border[6]; + float padding[6]; + YGDirection direction; + + uint32_t computedFlexBasisGeneration; + float computedFlexBasis; + bool hadOverflow; + + // Instead of recomputing the entire layout every single time, we + // cache some information to break early when nothing changed + uint32_t generationCount; + YGDirection lastParentDirection; + + uint32_t nextCachedMeasurementsIndex; + YGCachedMeasurement cachedMeasurements[YG_MAX_CACHED_RESULT_COUNT]; + float measuredDimensions[2]; + + YGCachedMeasurement cachedLayout; +} YGLayout; + +typedef struct YGStyle { + YGDirection direction; + YGFlexDirection flexDirection; + YGJustify justifyContent; + YGAlign alignContent; + YGAlign alignItems; + YGAlign alignSelf; + YGPositionType positionType; + YGWrap flexWrap; + YGOverflow overflow; + YGDisplay display; + float flex; + float flexGrow; + float flexShrink; + YGValue flexBasis; + YGValue margin[YGEdgeCount]; + YGValue position[YGEdgeCount]; + YGValue padding[YGEdgeCount]; + YGValue border[YGEdgeCount]; + YGValue dimensions[2]; + YGValue minDimensions[2]; + YGValue maxDimensions[2]; + + // Yoga specific properties, not compatible with flexbox specification + float aspectRatio; +} YGStyle; + +typedef struct YGConfig { + bool experimentalFeatures[YGExperimentalFeatureCount + 1]; + bool useWebDefaults; + bool useLegacyStretchBehaviour; + float pointScaleFactor; + YGLogger logger; + YGNodeClonedFunc cloneNodeCallback; + void* context; +} YGConfig; + +typedef struct YGNode { + YGStyle style; + YGLayout layout; + uint32_t lineIndex; + + YGNodeRef parent; + YGVector children; + + struct YGNode* nextChild; + + YGMeasureFunc measure; + YGBaselineFunc baseline; + YGPrintFunc print; + YGConfigRef config; + void* context; + + bool isDirty; + bool hasNewLayout; + YGNodeType nodeType; + + YGValue const* resolvedDimensions[2]; +} YGNode; + +#define YG_UNDEFINED_VALUES \ + { .value = YGUndefined, .unit = YGUnitUndefined } + +#define YG_AUTO_VALUES \ + { .value = YGUndefined, .unit = YGUnitAuto } + +#define YG_DEFAULT_EDGE_VALUES_UNIT \ + { \ + [YGEdgeLeft] = YG_UNDEFINED_VALUES, [YGEdgeTop] = YG_UNDEFINED_VALUES, \ + [YGEdgeRight] = YG_UNDEFINED_VALUES, [YGEdgeBottom] = YG_UNDEFINED_VALUES, \ + [YGEdgeStart] = YG_UNDEFINED_VALUES, [YGEdgeEnd] = YG_UNDEFINED_VALUES, \ + [YGEdgeHorizontal] = YG_UNDEFINED_VALUES, \ + [YGEdgeVertical] = YG_UNDEFINED_VALUES, [YGEdgeAll] = YG_UNDEFINED_VALUES, \ + } + +#define YG_DEFAULT_DIMENSION_VALUES \ + { [YGDimensionWidth] = YGUndefined, [YGDimensionHeight] = YGUndefined, } + +#define YG_DEFAULT_DIMENSION_VALUES_UNIT \ + { \ + [YGDimensionWidth] = YG_UNDEFINED_VALUES, \ + [YGDimensionHeight] = YG_UNDEFINED_VALUES, \ + } + +#define YG_DEFAULT_DIMENSION_VALUES_AUTO_UNIT \ + { [YGDimensionWidth] = YG_AUTO_VALUES, [YGDimensionHeight] = YG_AUTO_VALUES, } + +static const float kDefaultFlexGrow = 0.0f; +static const float kDefaultFlexShrink = 0.0f; +static const float kWebDefaultFlexShrink = 1.0f; + +static const YGStyle gYGNodeStyleDefaults = { + .direction = YGDirectionInherit, + .flexDirection = YGFlexDirectionColumn, + .justifyContent = YGJustifyFlexStart, + .alignContent = YGAlignFlexStart, + .alignItems = YGAlignStretch, + .alignSelf = YGAlignAuto, + .positionType = YGPositionTypeRelative, + .flexWrap = YGWrapNoWrap, + .overflow = YGOverflowVisible, + .display = YGDisplayFlex, + .flex = YGUndefined, + .flexGrow = YGUndefined, + .flexShrink = YGUndefined, + .flexBasis = YG_AUTO_VALUES, + .margin = YG_DEFAULT_EDGE_VALUES_UNIT, + .position = YG_DEFAULT_EDGE_VALUES_UNIT, + .padding = YG_DEFAULT_EDGE_VALUES_UNIT, + .border = YG_DEFAULT_EDGE_VALUES_UNIT, + .dimensions = YG_DEFAULT_DIMENSION_VALUES_AUTO_UNIT, + .minDimensions = YG_DEFAULT_DIMENSION_VALUES_UNIT, + .maxDimensions = YG_DEFAULT_DIMENSION_VALUES_UNIT, + .aspectRatio = YGUndefined, +}; + +static const YGLayout gYGNodeLayoutDefaults = { + .position = {}, + .dimensions = YG_DEFAULT_DIMENSION_VALUES, + .margin = {}, + .border = {}, + .padding = {}, + .direction = YGDirectionInherit, + .computedFlexBasisGeneration = 0, + .computedFlexBasis = YGUndefined, + .hadOverflow = false, + .generationCount = 0, + .lastParentDirection = (YGDirection)-1, + .nextCachedMeasurementsIndex = 0, + .cachedMeasurements = {}, + .measuredDimensions = YG_DEFAULT_DIMENSION_VALUES, + .cachedLayout = + { + .availableWidth = 0, + .availableHeight = 0, + .widthMeasureMode = (YGMeasureMode)-1, + .heightMeasureMode = (YGMeasureMode)-1, + .computedWidth = -1, + .computedHeight = -1, + }, +}; + +static const YGNode gYGNodeDefaults = { + .style = gYGNodeStyleDefaults, + .layout = gYGNodeLayoutDefaults, + .lineIndex = 0, + .parent = nullptr, + .children = YGVector(), + .nextChild = nullptr, + .measure = nullptr, + .baseline = nullptr, + .print = nullptr, + .config = nullptr, + .context = nullptr, + .isDirty = false, + .hasNewLayout = true, + .nodeType = YGNodeTypeDefault, + .resolvedDimensions = {[YGDimensionWidth] = &YGValueUndefined, + [YGDimensionHeight] = &YGValueUndefined}, +}; + +extern bool YGFloatsEqual(const float a, const float b); +extern bool YGValueEqual(const YGValue a, const YGValue b); +extern const YGValue* YGComputedEdgeValue( + const YGValue edges[YGEdgeCount], + const YGEdge edge, + const YGValue* const defaultValue);
− yoga/Yoga.c
@@ -1,3487 +0,0 @@-/**- * Copyright (c) 2014-present, Facebook, Inc.- * All rights reserved.- *- * This source code is licensed under the BSD-style license found in the- * LICENSE file in the root directory of this source tree. An additional grant- * of patent rights can be found in the PATENTS file in the same directory.- */--#include <string.h>--#include "YGNodeList.h"-#include "Yoga.h"--#ifdef _MSC_VER-#include <float.h>-#ifndef isnan-#define isnan _isnan-#endif--#ifndef __cplusplus-#define inline __inline-#endif--/* 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--typedef struct YGCachedMeasurement {- float availableWidth;- float availableHeight;- YGMeasureMode widthMeasureMode;- YGMeasureMode heightMeasureMode;-- float computedWidth;- float computedHeight;-} YGCachedMeasurement;--// This value was chosen based on empiracle data. Even the most complicated-// layouts should not require more than 16 entries to fit within the cache.-#define YG_MAX_CACHED_RESULT_COUNT 16--typedef struct YGLayout {- float position[4];- float dimensions[2];- float margin[6];- float border[6];- float padding[6];- YGDirection direction;-- uint32_t computedFlexBasisGeneration;- float computedFlexBasis;-- // Instead of recomputing the entire layout every single time, we- // cache some information to break early when nothing changed- uint32_t generationCount;- YGDirection lastParentDirection;-- uint32_t nextCachedMeasurementsIndex;- YGCachedMeasurement cachedMeasurements[YG_MAX_CACHED_RESULT_COUNT];- float measuredDimensions[2];-- YGCachedMeasurement cachedLayout;-} YGLayout;--typedef struct YGStyle {- YGDirection direction;- YGFlexDirection flexDirection;- YGJustify justifyContent;- YGAlign alignContent;- YGAlign alignItems;- YGAlign alignSelf;- YGPositionType positionType;- YGWrap flexWrap;- YGOverflow overflow;- YGDisplay display;- float flex;- float flexGrow;- float flexShrink;- YGValue flexBasis;- YGValue margin[YGEdgeCount];- YGValue position[YGEdgeCount];- YGValue padding[YGEdgeCount];- YGValue border[YGEdgeCount];- YGValue dimensions[2];- YGValue minDimensions[2];- YGValue maxDimensions[2];-- // Yoga specific properties, not compatible with flexbox specification- float aspectRatio;-} YGStyle;--typedef struct YGConfig {- bool experimentalFeatures[YGExperimentalFeatureCount + 1];- bool useWebDefaults;- float pointScaleFactor;-} YGConfig;--typedef struct YGNode {- YGStyle style;- YGLayout layout;- uint32_t lineIndex;-- YGNodeRef parent;- YGNodeListRef children;-- struct YGNode *nextChild;-- YGMeasureFunc measure;- YGBaselineFunc baseline;- YGPrintFunc print;- YGConfigRef config;- void *context;-- bool isDirty;- bool hasNewLayout;-- YGValue const *resolvedDimensions[2];-} YGNode;--#define YG_UNDEFINED_VALUES \- { .value = YGUndefined, .unit = YGUnitUndefined }--#define YG_AUTO_VALUES \- { .value = YGUndefined, .unit = YGUnitAuto }--#define YG_DEFAULT_EDGE_VALUES_UNIT \- { \- [YGEdgeLeft] = YG_UNDEFINED_VALUES, [YGEdgeTop] = YG_UNDEFINED_VALUES, \- [YGEdgeRight] = YG_UNDEFINED_VALUES, [YGEdgeBottom] = YG_UNDEFINED_VALUES, \- [YGEdgeStart] = YG_UNDEFINED_VALUES, [YGEdgeEnd] = YG_UNDEFINED_VALUES, \- [YGEdgeHorizontal] = YG_UNDEFINED_VALUES, [YGEdgeVertical] = YG_UNDEFINED_VALUES, \- [YGEdgeAll] = YG_UNDEFINED_VALUES, \- }--#define YG_DEFAULT_DIMENSION_VALUES \- { [YGDimensionWidth] = YGUndefined, [YGDimensionHeight] = YGUndefined, }--#define YG_DEFAULT_DIMENSION_VALUES_UNIT \- { [YGDimensionWidth] = YG_UNDEFINED_VALUES, [YGDimensionHeight] = YG_UNDEFINED_VALUES, }--#define YG_DEFAULT_DIMENSION_VALUES_AUTO_UNIT \- { [YGDimensionWidth] = YG_AUTO_VALUES, [YGDimensionHeight] = YG_AUTO_VALUES, }--static const float kDefaultFlexGrow = 0.0f;-static const float kDefaultFlexShrink = 0.0f;-static const float kWebDefaultFlexShrink = 1.0f;--static YGNode gYGNodeDefaults = {- .parent = NULL,- .children = NULL,- .hasNewLayout = true,- .isDirty = false,- .resolvedDimensions = {[YGDimensionWidth] = &YGValueUndefined,- [YGDimensionHeight] = &YGValueUndefined},-- .style =- {- .flex = YGUndefined,- .flexGrow = YGUndefined,- .flexShrink = YGUndefined,- .flexBasis = YG_AUTO_VALUES,- .justifyContent = YGJustifyFlexStart,- .alignItems = YGAlignStretch,- .alignContent = YGAlignFlexStart,- .direction = YGDirectionInherit,- .flexDirection = YGFlexDirectionColumn,- .overflow = YGOverflowVisible,- .display = YGDisplayFlex,- .dimensions = YG_DEFAULT_DIMENSION_VALUES_AUTO_UNIT,- .minDimensions = YG_DEFAULT_DIMENSION_VALUES_UNIT,- .maxDimensions = YG_DEFAULT_DIMENSION_VALUES_UNIT,- .position = YG_DEFAULT_EDGE_VALUES_UNIT,- .margin = YG_DEFAULT_EDGE_VALUES_UNIT,- .padding = YG_DEFAULT_EDGE_VALUES_UNIT,- .border = YG_DEFAULT_EDGE_VALUES_UNIT,- .aspectRatio = YGUndefined,- },-- .layout =- {- .dimensions = YG_DEFAULT_DIMENSION_VALUES,- .lastParentDirection = (YGDirection) -1,- .nextCachedMeasurementsIndex = 0,- .computedFlexBasis = YGUndefined,- .measuredDimensions = YG_DEFAULT_DIMENSION_VALUES,-- .cachedLayout =- {- .widthMeasureMode = (YGMeasureMode) -1,- .heightMeasureMode = (YGMeasureMode) -1,- .computedWidth = -1,- .computedHeight = -1,- },- },-};--static YGConfig gYGConfigDefaults = {- .experimentalFeatures =- {- [YGExperimentalFeatureRounding] = false,- [YGExperimentalFeatureMinFlexFix] = false,- [YGExperimentalFeatureWebFlexBasis] = false,- },- .useWebDefaults = false,- .pointScaleFactor = 1.0f-};--static void YGNodeMarkDirtyInternal(const YGNodeRef node);--YGMalloc gYGMalloc = &malloc;-YGCalloc gYGCalloc = &calloc;-YGRealloc gYGRealloc = &realloc;-YGFree gYGFree = &free;--static YGValue YGValueZero = {.value = 0, .unit = YGUnitPoint};--#ifdef ANDROID-#include <android/log.h>-static int YGAndroidLog(YGLogLevel level, const char *format, va_list args) {- int androidLevel = YGLogLevelDebug;- switch (level) {- case YGLogLevelError:- androidLevel = ANDROID_LOG_ERROR;- break;- case YGLogLevelWarn:- androidLevel = ANDROID_LOG_WARN;- break;- case YGLogLevelInfo:- androidLevel = ANDROID_LOG_INFO;- break;- case YGLogLevelDebug:- androidLevel = ANDROID_LOG_DEBUG;- break;- case YGLogLevelVerbose:- androidLevel = ANDROID_LOG_VERBOSE;- break;- }- const int result = __android_log_vprint(androidLevel, "YG-layout", format, args);- return result;-}-static YGLogger gLogger = &YGAndroidLog;-#else-static int YGDefaultLog(YGLogLevel level, const char *format, va_list args) {- switch (level) {- case YGLogLevelError:- return vfprintf(stderr, format, args);- case YGLogLevelWarn:- case YGLogLevelInfo:- case YGLogLevelDebug:- case YGLogLevelVerbose:- default:- return vprintf(format, args);- }-}-static YGLogger gLogger = &YGDefaultLog;-#endif--static inline const YGValue *YGComputedEdgeValue(const YGValue edges[YGEdgeCount],- const YGEdge edge,- const YGValue *const defaultValue) {- YG_ASSERT(edge <= YGEdgeEnd, "Cannot get computed value of multi-edge shorthands");-- if (edges[edge].unit != YGUnitUndefined) {- return &edges[edge];- }-- if ((edge == YGEdgeTop || edge == YGEdgeBottom) &&- edges[YGEdgeVertical].unit != YGUnitUndefined) {- return &edges[YGEdgeVertical];- }-- if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || edge == YGEdgeEnd) &&- edges[YGEdgeHorizontal].unit != YGUnitUndefined) {- return &edges[YGEdgeHorizontal];- }-- if (edges[YGEdgeAll].unit != YGUnitUndefined) {- return &edges[YGEdgeAll];- }-- if (edge == YGEdgeStart || edge == YGEdgeEnd) {- return &YGValueUndefined;- }-- return defaultValue;-}--static inline float YGResolveValue(const YGValue *const value, const float parentSize) {- switch (value->unit) {- case YGUnitUndefined:- case YGUnitAuto:- return YGUndefined;- case YGUnitPoint:- return value->value;- case YGUnitPercent:- return value->value * parentSize / 100.0f;- }- return YGUndefined;-}--static inline float YGResolveValueMargin(const YGValue *const value, const float parentSize) {- return value->unit == YGUnitAuto ? 0 : YGResolveValue(value, parentSize);-}--int32_t gNodeInstanceCount = 0;--WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {- const YGNodeRef node = gYGMalloc(sizeof(YGNode));- YG_ASSERT(node, "Could not allocate memory for node");- gNodeInstanceCount++;-- memcpy(node, &gYGNodeDefaults, sizeof(YGNode));- if (config->useWebDefaults) {- node->style.flexDirection = YGFlexDirectionRow;- node->style.alignContent = YGAlignStretch;- }- node->config = config;- return node;-}--YGNodeRef YGNodeNew(void) {- return YGNodeNewWithConfig(&gYGConfigDefaults);-}--void YGNodeFree(const YGNodeRef node) {- if (node->parent) {- YGNodeListDelete(node->parent->children, node);- node->parent = NULL;- }-- const uint32_t childCount = YGNodeGetChildCount(node);- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeGetChild(node, i);- child->parent = NULL;- }-- YGNodeListFree(node->children);- gYGFree(node);- gNodeInstanceCount--;-}--void YGNodeFreeRecursive(const YGNodeRef root) {- while (YGNodeGetChildCount(root) > 0) {- const YGNodeRef child = YGNodeGetChild(root, 0);- YGNodeRemoveChild(root, child);- YGNodeFreeRecursive(child);- }- YGNodeFree(root);-}--void YGNodeReset(const YGNodeRef node) {- YG_ASSERT(YGNodeGetChildCount(node) == 0,- "Cannot reset a node which still has children attached");- YG_ASSERT(node->parent == NULL, "Cannot reset a node still attached to a parent");-- YGNodeListFree(node->children);-- const YGConfigRef config = node->config;- memcpy(node, &gYGNodeDefaults, sizeof(YGNode));- if (config->useWebDefaults) {- node->style.flexDirection = YGFlexDirectionRow;- node->style.alignContent = YGAlignStretch;- }- node->config = config;-}--int32_t YGNodeGetInstanceCount(void) {- return gNodeInstanceCount;-}--YGConfigRef YGConfigNew(void) {- const YGConfigRef config = gYGMalloc(sizeof(YGConfig));- YG_ASSERT(config, "Could not allocate memory for config");- memcpy(config, &gYGConfigDefaults, sizeof(YGConfig));- return config;-}--void YGConfigFree(const YGConfigRef config) {- gYGFree(config);-}--static void YGNodeMarkDirtyInternal(const YGNodeRef node) {- if (!node->isDirty) {- node->isDirty = true;- node->layout.computedFlexBasis = YGUndefined;- if (node->parent) {- YGNodeMarkDirtyInternal(node->parent);- }- }-}--void YGNodeSetMeasureFunc(const YGNodeRef node, YGMeasureFunc measureFunc) {- if (measureFunc == NULL) {- node->measure = NULL;- } else {- YG_ASSERT(YGNodeGetChildCount(node) == 0,- "Cannot set measure function: Nodes with measure functions cannot have children.");- node->measure = measureFunc;- }-}--YGMeasureFunc YGNodeGetMeasureFunc(const YGNodeRef node) {- return node->measure;-}--void YGNodeSetBaselineFunc(const YGNodeRef node, YGBaselineFunc baselineFunc) {- node->baseline = baselineFunc;-}--YGBaselineFunc YGNodeGetBaselineFunc(const YGNodeRef node) {- return node->baseline;-}--void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) {- YG_ASSERT(child->parent == NULL, "Child already has a parent, it must be removed first.");- YG_ASSERT(node->measure == NULL,- "Cannot add child: Nodes with measure functions cannot have children.");- YGNodeListInsert(&node->children, child, index);- child->parent = node;- YGNodeMarkDirtyInternal(node);-}--void YGNodeRemoveChild(const YGNodeRef node, const YGNodeRef child) {- if (YGNodeListDelete(node->children, child) != NULL) {- child->layout = gYGNodeDefaults.layout; // layout is no longer valid- child->parent = NULL;- YGNodeMarkDirtyInternal(node);- }-}--YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) {- return YGNodeListGet(node->children, index);-}--YGNodeRef YGNodeGetParent(const YGNodeRef node) {- return node->parent;-}--inline uint32_t YGNodeGetChildCount(const YGNodeRef node) {- return YGNodeListCount(node->children);-}--void YGNodeMarkDirty(const YGNodeRef node) {- YG_ASSERT(node->measure != NULL,- "Only leaf nodes with custom measure functions"- "should manually mark themselves as dirty");- YGNodeMarkDirtyInternal(node);-}--bool YGNodeIsDirty(const YGNodeRef node) {- return node->isDirty;-}--void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) {- if (memcmp(&dstNode->style, &srcNode->style, sizeof(YGStyle)) != 0) {- memcpy(&dstNode->style, &srcNode->style, sizeof(YGStyle));- YGNodeMarkDirtyInternal(dstNode);- }-}--static inline float YGResolveFlexGrow(const YGNodeRef node) {- if (!YGFloatIsUndefined(node->style.flexGrow)) {- return node->style.flexGrow;- }- if (!YGFloatIsUndefined(node->style.flex) && node->style.flex > 0.0f) {- return node->style.flex;- }- return kDefaultFlexGrow;-}--float YGNodeStyleGetFlexGrow(const YGNodeRef node) {- return YGFloatIsUndefined(node->style.flexGrow) ? kDefaultFlexGrow : node->style.flexGrow;-}--float YGNodeStyleGetFlexShrink(const YGNodeRef node) {- return YGFloatIsUndefined(node->style.flexShrink) ? (node->config->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink) : node->style.flexShrink;-}--static inline float YGNodeResolveFlexShrink(const YGNodeRef node) {- if (!YGFloatIsUndefined(node->style.flexShrink)) {- return node->style.flexShrink;- }- if (!node->config->useWebDefaults && !YGFloatIsUndefined(node->style.flex) && node->style.flex < 0.0f) {- return -node->style.flex;- }- return node->config->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink;-}--static inline const YGValue *YGNodeResolveFlexBasisPtr(const YGNodeRef node) {- if (node->style.flexBasis.unit != YGUnitAuto && node->style.flexBasis.unit != YGUnitUndefined) {- return &node->style.flexBasis;- }- if (!YGFloatIsUndefined(node->style.flex) && node->style.flex > 0.0f) {- return node->config->useWebDefaults ? &YGValueAuto : &YGValueZero;- }- return &YGValueAuto;-}--#define YG_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \- void YGNodeSet##name(const YGNodeRef node, type paramName) { \- node->instanceName = paramName; \- } \- \- type YGNodeGet##name(const YGNodeRef node) { \- return node->instanceName; \- }--#define YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \- void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \- if (node->style.instanceName != paramName) { \- node->style.instanceName = paramName; \- YGNodeMarkDirtyInternal(node); \- } \- }--#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(type, name, paramName, instanceName) \- void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \- if (node->style.instanceName.value != paramName || \- node->style.instanceName.unit != YGUnitPoint) { \- node->style.instanceName.value = paramName; \- node->style.instanceName.unit = \- YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPoint; \- YGNodeMarkDirtyInternal(node); \- } \- } \- \- void YGNodeStyleSet##name##Percent(const YGNodeRef node, const type paramName) { \- if (node->style.instanceName.value != paramName || \- node->style.instanceName.unit != YGUnitPercent) { \- node->style.instanceName.value = paramName; \- node->style.instanceName.unit = \- YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \- YGNodeMarkDirtyInternal(node); \- } \- }--#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(type, name, paramName, instanceName) \- void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \- if (node->style.instanceName.value != paramName || \- node->style.instanceName.unit != YGUnitPoint) { \- node->style.instanceName.value = paramName; \- node->style.instanceName.unit = YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPoint; \- YGNodeMarkDirtyInternal(node); \- } \- } \- \- void YGNodeStyleSet##name##Percent(const YGNodeRef node, const type paramName) { \- if (node->style.instanceName.value != paramName || \- node->style.instanceName.unit != YGUnitPercent) { \- node->style.instanceName.value = paramName; \- node->style.instanceName.unit = YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \- YGNodeMarkDirtyInternal(node); \- } \- } \- \- void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \- if (node->style.instanceName.unit != YGUnitAuto) { \- node->style.instanceName.value = YGUndefined; \- node->style.instanceName.unit = YGUnitAuto; \- YGNodeMarkDirtyInternal(node); \- } \- }--#define YG_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \- YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \- \- type YGNodeStyleGet##name(const YGNodeRef node) { \- return node->style.instanceName; \- }--#define YG_NODE_STYLE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \- YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(float, name, paramName, instanceName) \- \- type YGNodeStyleGet##name(const YGNodeRef node) { \- return node->style.instanceName; \- }--#define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(type, name, paramName, instanceName) \- YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(float, name, paramName, instanceName) \- \- type YGNodeStyleGet##name(const YGNodeRef node) { \- return node->style.instanceName; \- }--#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \- void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \- if (node->style.instanceName[edge].unit != YGUnitAuto) { \- node->style.instanceName[edge].value = YGUndefined; \- node->style.instanceName[edge].unit = YGUnitAuto; \- YGNodeMarkDirtyInternal(node); \- } \- }--#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \- void YGNodeStyleSet##name(const YGNodeRef node, const YGEdge edge, const float paramName) { \- if (node->style.instanceName[edge].value != paramName || \- node->style.instanceName[edge].unit != YGUnitPoint) { \- node->style.instanceName[edge].value = paramName; \- node->style.instanceName[edge].unit = \- YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint; \- YGNodeMarkDirtyInternal(node); \- } \- } \- \- void YGNodeStyleSet##name##Percent(const YGNodeRef node, \- const YGEdge edge, \- const float paramName) { \- if (node->style.instanceName[edge].value != paramName || \- node->style.instanceName[edge].unit != YGUnitPercent) { \- node->style.instanceName[edge].value = paramName; \- node->style.instanceName[edge].unit = \- YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent; \- YGNodeMarkDirtyInternal(node); \- } \- } \- \- WIN_STRUCT(type) YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \- return WIN_STRUCT_REF(node->style.instanceName[edge]); \- }--#define YG_NODE_STYLE_EDGE_PROPERTY_IMPL(type, name, paramName, instanceName) \- void YGNodeStyleSet##name(const YGNodeRef node, const YGEdge edge, const float paramName) { \- if (node->style.instanceName[edge].value != paramName || \- node->style.instanceName[edge].unit != YGUnitPoint) { \- node->style.instanceName[edge].value = paramName; \- node->style.instanceName[edge].unit = \- YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint; \- YGNodeMarkDirtyInternal(node); \- } \- } \- \- float YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \- return node->style.instanceName[edge].value; \- }--#define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \- type YGNodeLayoutGet##name(const YGNodeRef node) { \- return node->layout.instanceName; \- }--#define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \- type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \- YG_ASSERT(edge <= YGEdgeEnd, "Cannot get layout properties of multi-edge shorthands"); \- \- if (edge == YGEdgeLeft) { \- if (node->layout.direction == YGDirectionRTL) { \- return node->layout.instanceName[YGEdgeEnd]; \- } else { \- return node->layout.instanceName[YGEdgeStart]; \- } \- } \- \- if (edge == YGEdgeRight) { \- if (node->layout.direction == YGDirectionRTL) { \- return node->layout.instanceName[YGEdgeStart]; \- } else { \- return node->layout.instanceName[YGEdgeEnd]; \- } \- } \- \- return node->layout.instanceName[edge]; \- }--YG_NODE_PROPERTY_IMPL(void *, Context, context, context);-YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print);-YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout);--YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction);-YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection);-YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent);-YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent);-YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems);-YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf);-YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType);-YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap);-YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow);-YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display);--YG_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex);-YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexGrow, flexGrow, flexGrow);-YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexShrink, flexShrink, flexShrink);-YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, FlexBasis, flexBasis, flexBasis);--YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position);-YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin);-YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin);-YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding);-YG_NODE_STYLE_EDGE_PROPERTY_IMPL(float, Border, border, border);--YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]);-YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]);-YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]);-YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]);-YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]);-YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]);--// Yoga specific properties, not compatible with flexbox specification-YG_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio);--YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]);-YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]);-YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]);-YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]);-YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]);-YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]);-YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction);--YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin);-YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border);-YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding);--uint32_t gCurrentGenerationCount = 0;--bool YGLayoutNodeInternal(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGDirection parentDirection,- const YGMeasureMode widthMeasureMode,- const YGMeasureMode heightMeasureMode,- const float parentWidth,- const float parentHeight,- const bool performLayout,- const char *reason,- const YGConfigRef config);--inline bool YGFloatIsUndefined(const float value) {- return isnan(value);-}--static inline bool YGValueEqual(const YGValue a, const YGValue b) {- if (a.unit != b.unit) {- return false;- }-- if (a.unit == YGUnitUndefined) {- return true;- }-- return fabs(a.value - b.value) < 0.0001f;-}--static inline void YGResolveDimensions(YGNodeRef node) {- for (YGDimension dim = YGDimensionWidth; dim <= YGDimensionHeight; dim++) {- if (node->style.maxDimensions[dim].unit != YGUnitUndefined &&- YGValueEqual(node->style.maxDimensions[dim], node->style.minDimensions[dim])) {- node->resolvedDimensions[dim] = &node->style.maxDimensions[dim];- } else {- node->resolvedDimensions[dim] = &node->style.dimensions[dim];- }- }-}--static inline bool YGFloatsEqual(const float a, const float b) {- if (YGFloatIsUndefined(a)) {- return YGFloatIsUndefined(b);- }- return fabs(a - b) < 0.0001f;-}--static void YGIndent(const uint32_t n) {- for (uint32_t i = 0; i < n; i++) {- YGLog(YGLogLevelDebug, " ");- }-}--static void YGPrintNumberIfNotZero(const char *str, const YGValue *const number) {- if (!YGFloatsEqual(number->value, 0)) {- YGLog(YGLogLevelDebug,- "%s: %g%s, ",- str,- number->value,- number->unit == YGUnitPoint ? "pt" : "%");- }-}--static void YGPrintNumberIfNotUndefinedf(const char *str, const float number) {- if (!YGFloatIsUndefined(number)) {- YGLog(YGLogLevelDebug, "%s: %g, ", str, number);- }-}--static void YGPrintNumberIfNotUndefined(const char *str, const YGValue *const number) {- if (number->unit != YGUnitUndefined) {- YGLog(YGLogLevelDebug,- "%s: %g%s, ",- str,- number->value,- number->unit == YGUnitPoint ? "pt" : "%");- }-}--static bool YGFourValuesEqual(const YGValue four[4]) {- return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) &&- YGValueEqual(four[0], four[3]);-}--static void YGNodePrintInternal(const YGNodeRef node,- const YGPrintOptions options,- const uint32_t level) {- YGIndent(level);- YGLog(YGLogLevelDebug, "{");-- if (node->print) {- node->print(node);- }-- if (options & YGPrintOptionsLayout) {- YGLog(YGLogLevelDebug, "layout: {");- YGLog(YGLogLevelDebug, "width: %g, ", node->layout.dimensions[YGDimensionWidth]);- YGLog(YGLogLevelDebug, "height: %g, ", node->layout.dimensions[YGDimensionHeight]);- YGLog(YGLogLevelDebug, "top: %g, ", node->layout.position[YGEdgeTop]);- YGLog(YGLogLevelDebug, "left: %g", node->layout.position[YGEdgeLeft]);- YGLog(YGLogLevelDebug, "}, ");- }-- if (options & YGPrintOptionsStyle) {- if (node->style.flexDirection == YGFlexDirectionColumn) {- YGLog(YGLogLevelDebug, "flexDirection: 'column', ");- } else if (node->style.flexDirection == YGFlexDirectionColumnReverse) {- YGLog(YGLogLevelDebug, "flexDirection: 'column-reverse', ");- } else if (node->style.flexDirection == YGFlexDirectionRow) {- YGLog(YGLogLevelDebug, "flexDirection: 'row', ");- } else if (node->style.flexDirection == YGFlexDirectionRowReverse) {- YGLog(YGLogLevelDebug, "flexDirection: 'row-reverse', ");- }-- if (node->style.justifyContent == YGJustifyCenter) {- YGLog(YGLogLevelDebug, "justifyContent: 'center', ");- } else if (node->style.justifyContent == YGJustifyFlexEnd) {- YGLog(YGLogLevelDebug, "justifyContent: 'flex-end', ");- } else if (node->style.justifyContent == YGJustifySpaceAround) {- YGLog(YGLogLevelDebug, "justifyContent: 'space-around', ");- } else if (node->style.justifyContent == YGJustifySpaceBetween) {- YGLog(YGLogLevelDebug, "justifyContent: 'space-between', ");- }-- if (node->style.alignItems == YGAlignCenter) {- YGLog(YGLogLevelDebug, "alignItems: 'center', ");- } else if (node->style.alignItems == YGAlignFlexEnd) {- YGLog(YGLogLevelDebug, "alignItems: 'flex-end', ");- } else if (node->style.alignItems == YGAlignStretch) {- YGLog(YGLogLevelDebug, "alignItems: 'stretch', ");- }-- if (node->style.alignContent == YGAlignCenter) {- YGLog(YGLogLevelDebug, "alignContent: 'center', ");- } else if (node->style.alignContent == YGAlignFlexEnd) {- YGLog(YGLogLevelDebug, "alignContent: 'flex-end', ");- } else if (node->style.alignContent == YGAlignStretch) {- YGLog(YGLogLevelDebug, "alignContent: 'stretch', ");- }-- if (node->style.alignSelf == YGAlignFlexStart) {- YGLog(YGLogLevelDebug, "alignSelf: 'flex-start', ");- } else if (node->style.alignSelf == YGAlignCenter) {- YGLog(YGLogLevelDebug, "alignSelf: 'center', ");- } else if (node->style.alignSelf == YGAlignFlexEnd) {- YGLog(YGLogLevelDebug, "alignSelf: 'flex-end', ");- } else if (node->style.alignSelf == YGAlignStretch) {- YGLog(YGLogLevelDebug, "alignSelf: 'stretch', ");- }-- YGPrintNumberIfNotUndefinedf("flexGrow", YGResolveFlexGrow(node));- YGPrintNumberIfNotUndefinedf("flexShrink", YGNodeResolveFlexShrink(node));- YGPrintNumberIfNotUndefined("flexBasis", YGNodeResolveFlexBasisPtr(node));-- if (node->style.overflow == YGOverflowHidden) {- YGLog(YGLogLevelDebug, "overflow: 'hidden', ");- } else if (node->style.overflow == YGOverflowVisible) {- YGLog(YGLogLevelDebug, "overflow: 'visible', ");- } else if (node->style.overflow == YGOverflowScroll) {- YGLog(YGLogLevelDebug, "overflow: 'scroll', ");- }-- if (YGFourValuesEqual(node->style.margin)) {- YGPrintNumberIfNotZero("margin",- YGComputedEdgeValue(node->style.margin, YGEdgeLeft, &YGValueZero));- } else {- YGPrintNumberIfNotZero("marginLeft",- YGComputedEdgeValue(node->style.margin, YGEdgeLeft, &YGValueZero));- YGPrintNumberIfNotZero("marginRight",- YGComputedEdgeValue(node->style.margin, YGEdgeRight, &YGValueZero));- YGPrintNumberIfNotZero("marginTop",- YGComputedEdgeValue(node->style.margin, YGEdgeTop, &YGValueZero));- YGPrintNumberIfNotZero("marginBottom",- YGComputedEdgeValue(node->style.margin, YGEdgeBottom, &YGValueZero));- YGPrintNumberIfNotZero("marginStart",- YGComputedEdgeValue(node->style.margin, YGEdgeStart, &YGValueZero));- YGPrintNumberIfNotZero("marginEnd",- YGComputedEdgeValue(node->style.margin, YGEdgeEnd, &YGValueZero));- }-- if (YGFourValuesEqual(node->style.padding)) {- YGPrintNumberIfNotZero("padding",- YGComputedEdgeValue(node->style.padding, YGEdgeLeft, &YGValueZero));- } else {- YGPrintNumberIfNotZero("paddingLeft",- YGComputedEdgeValue(node->style.padding, YGEdgeLeft, &YGValueZero));- YGPrintNumberIfNotZero("paddingRight",- YGComputedEdgeValue(node->style.padding, YGEdgeRight, &YGValueZero));- YGPrintNumberIfNotZero("paddingTop",- YGComputedEdgeValue(node->style.padding, YGEdgeTop, &YGValueZero));- YGPrintNumberIfNotZero("paddingBottom",- YGComputedEdgeValue(node->style.padding, YGEdgeBottom, &YGValueZero));- YGPrintNumberIfNotZero("paddingStart",- YGComputedEdgeValue(node->style.padding, YGEdgeStart, &YGValueZero));- YGPrintNumberIfNotZero("paddingEnd",- YGComputedEdgeValue(node->style.padding, YGEdgeEnd, &YGValueZero));- }-- if (YGFourValuesEqual(node->style.border)) {- YGPrintNumberIfNotZero("borderWidth",- YGComputedEdgeValue(node->style.border, YGEdgeLeft, &YGValueZero));- } else {- YGPrintNumberIfNotZero("borderLeftWidth",- YGComputedEdgeValue(node->style.border, YGEdgeLeft, &YGValueZero));- YGPrintNumberIfNotZero("borderRightWidth",- YGComputedEdgeValue(node->style.border, YGEdgeRight, &YGValueZero));- YGPrintNumberIfNotZero("borderTopWidth",- YGComputedEdgeValue(node->style.border, YGEdgeTop, &YGValueZero));- YGPrintNumberIfNotZero("borderBottomWidth",- YGComputedEdgeValue(node->style.border, YGEdgeBottom, &YGValueZero));- YGPrintNumberIfNotZero("borderStartWidth",- YGComputedEdgeValue(node->style.border, YGEdgeStart, &YGValueZero));- YGPrintNumberIfNotZero("borderEndWidth",- YGComputedEdgeValue(node->style.border, YGEdgeEnd, &YGValueZero));- }-- YGPrintNumberIfNotUndefined("width", &node->style.dimensions[YGDimensionWidth]);- YGPrintNumberIfNotUndefined("height", &node->style.dimensions[YGDimensionHeight]);- YGPrintNumberIfNotUndefined("maxWidth", &node->style.maxDimensions[YGDimensionWidth]);- YGPrintNumberIfNotUndefined("maxHeight", &node->style.maxDimensions[YGDimensionHeight]);- YGPrintNumberIfNotUndefined("minWidth", &node->style.minDimensions[YGDimensionWidth]);- YGPrintNumberIfNotUndefined("minHeight", &node->style.minDimensions[YGDimensionHeight]);-- if (node->style.positionType == YGPositionTypeAbsolute) {- YGLog(YGLogLevelDebug, "position: 'absolute', ");- }-- YGPrintNumberIfNotUndefined(- "left", YGComputedEdgeValue(node->style.position, YGEdgeLeft, &YGValueUndefined));- YGPrintNumberIfNotUndefined(- "right", YGComputedEdgeValue(node->style.position, YGEdgeRight, &YGValueUndefined));- YGPrintNumberIfNotUndefined(- "top", YGComputedEdgeValue(node->style.position, YGEdgeTop, &YGValueUndefined));- YGPrintNumberIfNotUndefined(- "bottom", YGComputedEdgeValue(node->style.position, YGEdgeBottom, &YGValueUndefined));- }-- const uint32_t childCount = YGNodeListCount(node->children);- if (options & YGPrintOptionsChildren && childCount > 0) {- YGLog(YGLogLevelDebug, "children: [\n");- for (uint32_t i = 0; i < childCount; i++) {- YGNodePrintInternal(YGNodeGetChild(node, i), options, level + 1);- }- YGIndent(level);- YGLog(YGLogLevelDebug, "]},\n");- } else {- YGLog(YGLogLevelDebug, "},\n");- }-}--void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) {- YGNodePrintInternal(node, options, 0);-}--static const YGEdge leading[4] = {- [YGFlexDirectionColumn] = YGEdgeTop,- [YGFlexDirectionColumnReverse] = YGEdgeBottom,- [YGFlexDirectionRow] = YGEdgeLeft,- [YGFlexDirectionRowReverse] = YGEdgeRight,-};-static const YGEdge trailing[4] = {- [YGFlexDirectionColumn] = YGEdgeBottom,- [YGFlexDirectionColumnReverse] = YGEdgeTop,- [YGFlexDirectionRow] = YGEdgeRight,- [YGFlexDirectionRowReverse] = YGEdgeLeft,-};-static const YGEdge pos[4] = {- [YGFlexDirectionColumn] = YGEdgeTop,- [YGFlexDirectionColumnReverse] = YGEdgeBottom,- [YGFlexDirectionRow] = YGEdgeLeft,- [YGFlexDirectionRowReverse] = YGEdgeRight,-};-static const YGDimension dim[4] = {- [YGFlexDirectionColumn] = YGDimensionHeight,- [YGFlexDirectionColumnReverse] = YGDimensionHeight,- [YGFlexDirectionRow] = YGDimensionWidth,- [YGFlexDirectionRowReverse] = YGDimensionWidth,-};--static inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) {- return flexDirection == YGFlexDirectionRow || flexDirection == YGFlexDirectionRowReverse;-}--static inline bool YGFlexDirectionIsColumn(const YGFlexDirection flexDirection) {- return flexDirection == YGFlexDirectionColumn || flexDirection == YGFlexDirectionColumnReverse;-}--static inline float YGNodeLeadingMargin(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeStart].unit != YGUnitUndefined) {- return YGResolveValueMargin(&node->style.margin[YGEdgeStart], widthSize);- }-- return YGResolveValueMargin(YGComputedEdgeValue(node->style.margin, leading[axis], &YGValueZero),- widthSize);-}--static float YGNodeTrailingMargin(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeEnd].unit != YGUnitUndefined) {- return YGResolveValueMargin(&node->style.margin[YGEdgeEnd], widthSize);- }-- return YGResolveValueMargin(YGComputedEdgeValue(node->style.margin, trailing[axis], &YGValueZero),- widthSize);-}--static float YGNodeLeadingPadding(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- if (YGFlexDirectionIsRow(axis) && node->style.padding[YGEdgeStart].unit != YGUnitUndefined &&- YGResolveValue(&node->style.padding[YGEdgeStart], widthSize) >= 0.0f) {- return YGResolveValue(&node->style.padding[YGEdgeStart], widthSize);- }-- return fmaxf(YGResolveValue(YGComputedEdgeValue(node->style.padding, leading[axis], &YGValueZero),- widthSize),- 0.0f);-}--static float YGNodeTrailingPadding(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- if (YGFlexDirectionIsRow(axis) && node->style.padding[YGEdgeEnd].unit != YGUnitUndefined &&- YGResolveValue(&node->style.padding[YGEdgeEnd], widthSize) >= 0.0f) {- return YGResolveValue(&node->style.padding[YGEdgeEnd], widthSize);- }-- return fmaxf(YGResolveValue(YGComputedEdgeValue(node->style.padding, trailing[axis], &YGValueZero),- widthSize),- 0.0f);-}--static float YGNodeLeadingBorder(const YGNodeRef node, const YGFlexDirection axis) {- if (YGFlexDirectionIsRow(axis) && node->style.border[YGEdgeStart].unit != YGUnitUndefined &&- node->style.border[YGEdgeStart].value >= 0.0f) {- return node->style.border[YGEdgeStart].value;- }-- return fmaxf(YGComputedEdgeValue(node->style.border, leading[axis], &YGValueZero)->value, 0.0f);-}--static float YGNodeTrailingBorder(const YGNodeRef node, const YGFlexDirection axis) {- if (YGFlexDirectionIsRow(axis) && node->style.border[YGEdgeEnd].unit != YGUnitUndefined &&- node->style.border[YGEdgeEnd].value >= 0.0f) {- return node->style.border[YGEdgeEnd].value;- }-- return fmaxf(YGComputedEdgeValue(node->style.border, trailing[axis], &YGValueZero)->value, 0.0f);-}--static inline float YGNodeLeadingPaddingAndBorder(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- return YGNodeLeadingPadding(node, axis, widthSize) + YGNodeLeadingBorder(node, axis);-}--static inline float YGNodeTrailingPaddingAndBorder(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- return YGNodeTrailingPadding(node, axis, widthSize) + YGNodeTrailingBorder(node, axis);-}--static inline float YGNodeMarginForAxis(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- return YGNodeLeadingMargin(node, axis, widthSize) + YGNodeTrailingMargin(node, axis, widthSize);-}--static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- return YGNodeLeadingPaddingAndBorder(node, axis, widthSize) +- YGNodeTrailingPaddingAndBorder(node, axis, widthSize);-}--static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) {- const YGAlign align =- child->style.alignSelf == YGAlignAuto ? node->style.alignItems : child->style.alignSelf;- if (align == YGAlignBaseline && YGFlexDirectionIsColumn(node->style.flexDirection)) {- return YGAlignFlexStart;- }- return align;-}--static inline YGDirection YGNodeResolveDirection(const YGNodeRef node,- const YGDirection parentDirection) {- if (node->style.direction == YGDirectionInherit) {- return parentDirection > YGDirectionInherit ? parentDirection : YGDirectionLTR;- } else {- return node->style.direction;- }-}--static float YGBaseline(const YGNodeRef node) {- if (node->baseline != NULL) {- const float baseline = node->baseline(node,- node->layout.measuredDimensions[YGDimensionWidth],- node->layout.measuredDimensions[YGDimensionHeight]);- YG_ASSERT(!YGFloatIsUndefined(baseline), "Expect custom baseline function to not return NaN")- return baseline;- }-- YGNodeRef baselineChild = NULL;- const uint32_t childCount = YGNodeGetChildCount(node);- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeGetChild(node, i);- if (child->lineIndex > 0) {- break;- }- if (child->style.positionType == YGPositionTypeAbsolute) {- continue;- }- if (YGNodeAlignItem(node, child) == YGAlignBaseline) {- baselineChild = child;- break;- }-- if (baselineChild == NULL) {- baselineChild = child;- }- }-- if (baselineChild == NULL) {- return node->layout.measuredDimensions[YGDimensionHeight];- }-- const float baseline = YGBaseline(baselineChild);- return baseline + baselineChild->layout.position[YGEdgeTop];-}--static inline YGFlexDirection YGResolveFlexDirection(const YGFlexDirection flexDirection,- const YGDirection direction) {- if (direction == YGDirectionRTL) {- if (flexDirection == YGFlexDirectionRow) {- return YGFlexDirectionRowReverse;- } else if (flexDirection == YGFlexDirectionRowReverse) {- return YGFlexDirectionRow;- }- }-- return flexDirection;-}--static YGFlexDirection YGFlexDirectionCross(const YGFlexDirection flexDirection,- const YGDirection direction) {- return YGFlexDirectionIsColumn(flexDirection)- ? YGResolveFlexDirection(YGFlexDirectionRow, direction)- : YGFlexDirectionColumn;-}--static inline bool YGNodeIsFlex(const YGNodeRef node) {- return (node->style.positionType == YGPositionTypeRelative &&- (YGResolveFlexGrow(node) != 0 || YGNodeResolveFlexShrink(node) != 0));-}--static bool YGIsBaselineLayout(const YGNodeRef node) {- if (YGFlexDirectionIsColumn(node->style.flexDirection)) {- return false;- }- if (node->style.alignItems == YGAlignBaseline) {- return true;- }- const uint32_t childCount = YGNodeGetChildCount(node);- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeGetChild(node, i);- if (child->style.positionType == YGPositionTypeRelative &&- child->style.alignSelf == YGAlignBaseline) {- return true;- }- }-- return false;-}--static inline float YGNodeDimWithMargin(const YGNodeRef node,- const YGFlexDirection axis,- const float widthSize) {- return node->layout.measuredDimensions[dim[axis]] + YGNodeLeadingMargin(node, axis, widthSize) +- YGNodeTrailingMargin(node, axis, widthSize);-}--static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node,- const YGFlexDirection axis,- const float parentSize) {- return !(node->resolvedDimensions[dim[axis]]->unit == YGUnitAuto ||- node->resolvedDimensions[dim[axis]]->unit == YGUnitUndefined ||- (node->resolvedDimensions[dim[axis]]->unit == YGUnitPoint &&- node->resolvedDimensions[dim[axis]]->value < 0.0f) ||- (node->resolvedDimensions[dim[axis]]->unit == YGUnitPercent &&- (node->resolvedDimensions[dim[axis]]->value < 0.0f || YGFloatIsUndefined(parentSize))));-}--static inline bool YGNodeIsLayoutDimDefined(const YGNodeRef node, const YGFlexDirection axis) {- const float value = node->layout.measuredDimensions[dim[axis]];- return !YGFloatIsUndefined(value) && value >= 0.0f;-}--static inline bool YGNodeIsLeadingPosDefined(const YGNodeRef node, const YGFlexDirection axis) {- return (YGFlexDirectionIsRow(axis) &&- YGComputedEdgeValue(node->style.position, YGEdgeStart, &YGValueUndefined)->unit !=- YGUnitUndefined) ||- YGComputedEdgeValue(node->style.position, leading[axis], &YGValueUndefined)->unit !=- YGUnitUndefined;-}--static inline bool YGNodeIsTrailingPosDefined(const YGNodeRef node, const YGFlexDirection axis) {- return (YGFlexDirectionIsRow(axis) &&- YGComputedEdgeValue(node->style.position, YGEdgeEnd, &YGValueUndefined)->unit !=- YGUnitUndefined) ||- YGComputedEdgeValue(node->style.position, trailing[axis], &YGValueUndefined)->unit !=- YGUnitUndefined;-}--static float YGNodeLeadingPosition(const YGNodeRef node,- const YGFlexDirection axis,- const float axisSize) {- if (YGFlexDirectionIsRow(axis)) {- const YGValue *leadingPosition =- YGComputedEdgeValue(node->style.position, YGEdgeStart, &YGValueUndefined);- if (leadingPosition->unit != YGUnitUndefined) {- return YGResolveValue(leadingPosition, axisSize);- }- }-- const YGValue *leadingPosition =- YGComputedEdgeValue(node->style.position, leading[axis], &YGValueUndefined);-- return leadingPosition->unit == YGUnitUndefined ? 0.0f- : YGResolveValue(leadingPosition, axisSize);-}--static float YGNodeTrailingPosition(const YGNodeRef node,- const YGFlexDirection axis,- const float axisSize) {- if (YGFlexDirectionIsRow(axis)) {- const YGValue *trailingPosition =- YGComputedEdgeValue(node->style.position, YGEdgeEnd, &YGValueUndefined);- if (trailingPosition->unit != YGUnitUndefined) {- return YGResolveValue(trailingPosition, axisSize);- }- }-- const YGValue *trailingPosition =- YGComputedEdgeValue(node->style.position, trailing[axis], &YGValueUndefined);-- return trailingPosition->unit == YGUnitUndefined ? 0.0f- : YGResolveValue(trailingPosition, axisSize);-}--static float YGNodeBoundAxisWithinMinAndMax(const YGNodeRef node,- const YGFlexDirection axis,- const float value,- const float axisSize) {- float min = YGUndefined;- float max = YGUndefined;-- if (YGFlexDirectionIsColumn(axis)) {- min = YGResolveValue(&node->style.minDimensions[YGDimensionHeight], axisSize);- max = YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], axisSize);- } else if (YGFlexDirectionIsRow(axis)) {- min = YGResolveValue(&node->style.minDimensions[YGDimensionWidth], axisSize);- max = YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], axisSize);- }-- float boundValue = value;-- if (!YGFloatIsUndefined(max) && max >= 0.0f && boundValue > max) {- boundValue = max;- }-- if (!YGFloatIsUndefined(min) && min >= 0.0f && boundValue < min) {- boundValue = min;- }-- return boundValue;-}--static inline YGValue *YGMarginLeadingValue(const YGNodeRef node, const YGFlexDirection axis) {- if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeStart].unit != YGUnitUndefined) {- return &node->style.margin[YGEdgeStart];- } else {- return &node->style.margin[leading[axis]];- }-}--static inline YGValue *YGMarginTrailingValue(const YGNodeRef node, const YGFlexDirection axis) {- if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeEnd].unit != YGUnitUndefined) {- return &node->style.margin[YGEdgeEnd];- } else {- return &node->style.margin[trailing[axis]];- }-}--// Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't go-// below the-// padding and border amount.-static inline float YGNodeBoundAxis(const YGNodeRef node,- const YGFlexDirection axis,- const float value,- const float axisSize,- const float widthSize) {- return fmaxf(YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize),- YGNodePaddingAndBorderForAxis(node, axis, widthSize));-}--static void YGNodeSetChildTrailingPosition(const YGNodeRef node,- const YGNodeRef child,- const YGFlexDirection axis) {- const float size = child->layout.measuredDimensions[dim[axis]];- child->layout.position[trailing[axis]] =- node->layout.measuredDimensions[dim[axis]] - size - child->layout.position[pos[axis]];-}--// If both left and right are defined, then use left. Otherwise return-// +left or -right depending on which is defined.-static float YGNodeRelativePosition(const YGNodeRef node,- const YGFlexDirection axis,- const float axisSize) {- return YGNodeIsLeadingPosDefined(node, axis) ? YGNodeLeadingPosition(node, axis, axisSize)- : -YGNodeTrailingPosition(node, axis, axisSize);-}--static void YGConstrainMaxSizeForMode(const YGNodeRef node,- const enum YGFlexDirection axis,- const float parentAxisSize,- const float parentWidth,- YGMeasureMode *mode,- float *size) {- const float maxSize = YGResolveValue(&node->style.maxDimensions[dim[axis]], parentAxisSize) +- YGNodeMarginForAxis(node, axis, parentWidth);- switch (*mode) {- case YGMeasureModeExactly:- case YGMeasureModeAtMost:- *size = (YGFloatIsUndefined(maxSize) || *size < maxSize) ? *size : maxSize;- break;- case YGMeasureModeUndefined:- if (!YGFloatIsUndefined(maxSize)) {- *mode = YGMeasureModeAtMost;- *size = maxSize;- }- break;- }-}--static void YGNodeSetPosition(const YGNodeRef node,- const YGDirection direction,- const float mainSize,- const float crossSize,- const float parentWidth) {- const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction);- const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);- const float relativePositionMain = YGNodeRelativePosition(node, mainAxis, mainSize);- const float relativePositionCross = YGNodeRelativePosition(node, crossAxis, crossSize);-- node->layout.position[leading[mainAxis]] =- YGNodeLeadingMargin(node, mainAxis, parentWidth) + relativePositionMain;- node->layout.position[trailing[mainAxis]] =- YGNodeTrailingMargin(node, mainAxis, parentWidth) + relativePositionMain;- node->layout.position[leading[crossAxis]] =- YGNodeLeadingMargin(node, crossAxis, parentWidth) + relativePositionCross;- node->layout.position[trailing[crossAxis]] =- YGNodeTrailingMargin(node, crossAxis, parentWidth) + relativePositionCross;-}--static void YGNodeComputeFlexBasisForChild(const YGNodeRef node,- const YGNodeRef child,- const float width,- const YGMeasureMode widthMode,- const float height,- const float parentWidth,- const float parentHeight,- const YGMeasureMode heightMode,- const YGDirection direction,- const YGConfigRef config) {- const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction);- const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);- const float mainAxisSize = isMainAxisRow ? width : height;- const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight;-- float childWidth;- float childHeight;- YGMeasureMode childWidthMeasureMode;- YGMeasureMode childHeightMeasureMode;-- const float resolvedFlexBasis =- YGResolveValue(YGNodeResolveFlexBasisPtr(child), mainAxisParentSize);- const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, parentWidth);- const bool isColumnStyleDimDefined =- YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, parentHeight);-- if (!YGFloatIsUndefined(resolvedFlexBasis) && !YGFloatIsUndefined(mainAxisSize)) {- if (YGFloatIsUndefined(child->layout.computedFlexBasis) ||- (YGConfigIsExperimentalFeatureEnabled(child->config, YGExperimentalFeatureWebFlexBasis) &&- child->layout.computedFlexBasisGeneration != gCurrentGenerationCount)) {- child->layout.computedFlexBasis =- fmaxf(resolvedFlexBasis, YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth));- }- } else if (isMainAxisRow && isRowStyleDimDefined) {- // The width is definite, so use that as the flex basis.- child->layout.computedFlexBasis =- fmaxf(YGResolveValue(child->resolvedDimensions[YGDimensionWidth], parentWidth),- YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth));- } else if (!isMainAxisRow && isColumnStyleDimDefined) {- // The height is definite, so use that as the flex basis.- child->layout.computedFlexBasis =- fmaxf(YGResolveValue(child->resolvedDimensions[YGDimensionHeight], parentHeight),- YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, parentWidth));- } else {- // Compute the flex basis and hypothetical main size (i.e. the clamped- // flex basis).- childWidth = YGUndefined;- childHeight = YGUndefined;- childWidthMeasureMode = YGMeasureModeUndefined;- childHeightMeasureMode = YGMeasureModeUndefined;-- const float marginRow = YGNodeMarginForAxis(child, YGFlexDirectionRow, parentWidth);- const float marginColumn = YGNodeMarginForAxis(child, YGFlexDirectionColumn, parentWidth);-- if (isRowStyleDimDefined) {- childWidth =- YGResolveValue(child->resolvedDimensions[YGDimensionWidth], parentWidth) + marginRow;- childWidthMeasureMode = YGMeasureModeExactly;- }- if (isColumnStyleDimDefined) {- childHeight =- YGResolveValue(child->resolvedDimensions[YGDimensionHeight], parentHeight) + marginColumn;- childHeightMeasureMode = YGMeasureModeExactly;- }-- // The W3C spec doesn't say anything about the 'overflow' property,- // but all major browsers appear to implement the following logic.- if ((!isMainAxisRow && node->style.overflow == YGOverflowScroll) ||- node->style.overflow != YGOverflowScroll) {- if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) {- childWidth = width;- childWidthMeasureMode = YGMeasureModeAtMost;- }- }-- if ((isMainAxisRow && node->style.overflow == YGOverflowScroll) ||- node->style.overflow != YGOverflowScroll) {- if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) {- childHeight = height;- childHeightMeasureMode = YGMeasureModeAtMost;- }- }-- // 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 && !YGFloatIsUndefined(width) && !isRowStyleDimDefined &&- widthMode == YGMeasureModeExactly && YGNodeAlignItem(node, child) == YGAlignStretch) {- childWidth = width;- childWidthMeasureMode = YGMeasureModeExactly;- }- if (isMainAxisRow && !YGFloatIsUndefined(height) && !isColumnStyleDimDefined &&- heightMode == YGMeasureModeExactly && YGNodeAlignItem(node, child) == YGAlignStretch) {- childHeight = height;- childHeightMeasureMode = YGMeasureModeExactly;- }-- if (!YGFloatIsUndefined(child->style.aspectRatio)) {- if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) {- child->layout.computedFlexBasis =- fmaxf((childWidth - marginRow) / child->style.aspectRatio,- YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, parentWidth));- return;- } else if (isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) {- child->layout.computedFlexBasis =- fmaxf((childHeight - marginColumn) * child->style.aspectRatio,- YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth));- return;- }- }-- YGConstrainMaxSizeForMode(- child, YGFlexDirectionRow, parentWidth, parentWidth, &childWidthMeasureMode, &childWidth);- YGConstrainMaxSizeForMode(- child, YGFlexDirectionColumn, parentHeight, parentWidth, &childHeightMeasureMode, &childHeight);-- // Measure the child- YGLayoutNodeInternal(child,- childWidth,- childHeight,- direction,- childWidthMeasureMode,- childHeightMeasureMode,- parentWidth,- parentHeight,- false,- "measure",- config);-- child->layout.computedFlexBasis =- fmaxf(child->layout.measuredDimensions[dim[mainAxis]],- YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth));- }-- child->layout.computedFlexBasisGeneration = gCurrentGenerationCount;-}--static void YGNodeAbsoluteLayoutChild(const YGNodeRef node,- const YGNodeRef child,- const float width,- const YGMeasureMode widthMode,- const float height,- const YGDirection direction,- const YGConfigRef config) {- const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction);- const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);- const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);-- float childWidth = YGUndefined;- float childHeight = YGUndefined;- YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined;- YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined;-- const float marginRow = YGNodeMarginForAxis(child, YGFlexDirectionRow, width);- const float marginColumn = YGNodeMarginForAxis(child, YGFlexDirectionColumn, width);-- if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) {- childWidth = YGResolveValue(child->resolvedDimensions[YGDimensionWidth], width) + marginRow;- } else {- // If the child doesn't have a specified width, compute the width based- // on the left/right- // offsets if they're defined.- if (YGNodeIsLeadingPosDefined(child, YGFlexDirectionRow) &&- YGNodeIsTrailingPosDefined(child, YGFlexDirectionRow)) {- childWidth = node->layout.measuredDimensions[YGDimensionWidth] -- (YGNodeLeadingBorder(node, YGFlexDirectionRow) +- YGNodeTrailingBorder(node, YGFlexDirectionRow)) -- (YGNodeLeadingPosition(child, YGFlexDirectionRow, width) +- YGNodeTrailingPosition(child, YGFlexDirectionRow, width));- childWidth = YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width);- }- }-- if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) {- childHeight =- YGResolveValue(child->resolvedDimensions[YGDimensionHeight], height) + marginColumn;- } else {- // If the child doesn't have a specified height, compute the height- // based on the top/bottom- // offsets if they're defined.- if (YGNodeIsLeadingPosDefined(child, YGFlexDirectionColumn) &&- YGNodeIsTrailingPosDefined(child, YGFlexDirectionColumn)) {- childHeight = node->layout.measuredDimensions[YGDimensionHeight] -- (YGNodeLeadingBorder(node, YGFlexDirectionColumn) +- YGNodeTrailingBorder(node, YGFlexDirectionColumn)) -- (YGNodeLeadingPosition(child, YGFlexDirectionColumn, height) +- YGNodeTrailingPosition(child, YGFlexDirectionColumn, height));- childHeight = YGNodeBoundAxis(child, YGFlexDirectionColumn, childHeight, height, width);- }- }-- // Exactly one dimension needs to be defined for us to be able to do aspect ratio- // calculation. One dimension being the anchor and the other being flexible.- if (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) {- if (!YGFloatIsUndefined(child->style.aspectRatio)) {- if (YGFloatIsUndefined(childWidth)) {- childWidth =- marginRow + fmaxf((childHeight - marginColumn) * child->style.aspectRatio,- YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, width));- } else if (YGFloatIsUndefined(childHeight)) {- childHeight =- marginColumn + fmaxf((childWidth - marginRow) / child->style.aspectRatio,- YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, width));- }- }- }-- // If we're still missing one or the other dimension, measure the content.- if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) {- childWidthMeasureMode =- YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly;- childHeightMeasureMode =- YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly;-- // If the size of the parent is defined then try to constrain the absolute child to that size- // as well. This allows text within the absolute child to wrap to the size of its parent.- // This is the same behavior as many browsers implement.- if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && widthMode != YGMeasureModeUndefined &&- width > 0) {- childWidth = width;- childWidthMeasureMode = YGMeasureModeAtMost;- }-- YGLayoutNodeInternal(child,- childWidth,- childHeight,- direction,- childWidthMeasureMode,- childHeightMeasureMode,- childWidth,- childHeight,- false,- "abs-measure",- config);- childWidth = child->layout.measuredDimensions[YGDimensionWidth] +- YGNodeMarginForAxis(child, YGFlexDirectionRow, width);- childHeight = child->layout.measuredDimensions[YGDimensionHeight] +- YGNodeMarginForAxis(child, YGFlexDirectionColumn, width);- }-- YGLayoutNodeInternal(child,- childWidth,- childHeight,- direction,- YGMeasureModeExactly,- YGMeasureModeExactly,- childWidth,- childHeight,- true,- "abs-layout",- config);-- if (YGNodeIsTrailingPosDefined(child, mainAxis) && !YGNodeIsLeadingPosDefined(child, mainAxis)) {- child->layout.position[leading[mainAxis]] = node->layout.measuredDimensions[dim[mainAxis]] -- child->layout.measuredDimensions[dim[mainAxis]] -- YGNodeTrailingBorder(node, mainAxis) -- YGNodeTrailingPosition(child, mainAxis, width);- } else if (!YGNodeIsLeadingPosDefined(child, mainAxis) &&- node->style.justifyContent == YGJustifyCenter) {- child->layout.position[leading[mainAxis]] = (node->layout.measuredDimensions[dim[mainAxis]] -- child->layout.measuredDimensions[dim[mainAxis]]) /- 2.0f;- } else if (!YGNodeIsLeadingPosDefined(child, mainAxis) &&- node->style.justifyContent == YGJustifyFlexEnd) {- child->layout.position[leading[mainAxis]] = (node->layout.measuredDimensions[dim[mainAxis]] -- child->layout.measuredDimensions[dim[mainAxis]]);- }-- if (YGNodeIsTrailingPosDefined(child, crossAxis) &&- !YGNodeIsLeadingPosDefined(child, crossAxis)) {- child->layout.position[leading[crossAxis]] = node->layout.measuredDimensions[dim[crossAxis]] -- child->layout.measuredDimensions[dim[crossAxis]] -- YGNodeTrailingBorder(node, crossAxis) -- YGNodeTrailingPosition(child, crossAxis, width);- } else if (!YGNodeIsLeadingPosDefined(child, crossAxis) &&- YGNodeAlignItem(node, child) == YGAlignCenter) {- child->layout.position[leading[crossAxis]] =- (node->layout.measuredDimensions[dim[crossAxis]] -- child->layout.measuredDimensions[dim[crossAxis]]) /- 2.0f;- } else if (!YGNodeIsLeadingPosDefined(child, crossAxis) &&- YGNodeAlignItem(node, child) == YGAlignFlexEnd) {- child->layout.position[leading[crossAxis]] = (node->layout.measuredDimensions[dim[crossAxis]] -- child->layout.measuredDimensions[dim[crossAxis]]);- }-}--static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGMeasureMode widthMeasureMode,- const YGMeasureMode heightMeasureMode,- const float parentWidth,- const float parentHeight) {- YG_ASSERT(node->measure, "Expected node to have custom measure function");-- const float paddingAndBorderAxisRow =- YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth);- const float paddingAndBorderAxisColumn =- YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, availableWidth);- const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, availableWidth);- const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, availableWidth);-- const float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;- const float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;-- if (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly) {- // Don't bother sizing the text if both dimensions are already defined.- node->layout.measuredDimensions[YGDimensionWidth] = YGNodeBoundAxis(- node, YGFlexDirectionRow, availableWidth - marginAxisRow, parentWidth, parentWidth);- node->layout.measuredDimensions[YGDimensionHeight] = YGNodeBoundAxis(- node, YGFlexDirectionColumn, availableHeight - marginAxisColumn, parentHeight, parentWidth);- } else if (innerWidth <= 0.0f || innerHeight <= 0.0f) {- // Don't bother sizing the text if there's no horizontal or vertical- // space.- node->layout.measuredDimensions[YGDimensionWidth] =- YGNodeBoundAxis(node, YGFlexDirectionRow, 0.0f, availableWidth, availableWidth);- node->layout.measuredDimensions[YGDimensionHeight] =- YGNodeBoundAxis(node, YGFlexDirectionColumn, 0.0f, availableHeight, availableWidth);- } else {- // Measure the text under the current constraints.- const YGSize measuredSize =- node->measure(node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode);-- node->layout.measuredDimensions[YGDimensionWidth] =- YGNodeBoundAxis(node,- YGFlexDirectionRow,- (widthMeasureMode == YGMeasureModeUndefined ||- widthMeasureMode == YGMeasureModeAtMost)- ? measuredSize.width + paddingAndBorderAxisRow- : availableWidth - marginAxisRow,- availableWidth,- availableWidth);- node->layout.measuredDimensions[YGDimensionHeight] =- YGNodeBoundAxis(node,- YGFlexDirectionColumn,- (heightMeasureMode == YGMeasureModeUndefined ||- heightMeasureMode == YGMeasureModeAtMost)- ? measuredSize.height + paddingAndBorderAxisColumn- : availableHeight - marginAxisColumn,- availableHeight,- availableWidth);- }-}--// 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.-static void YGNodeEmptyContainerSetMeasuredDimensions(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGMeasureMode widthMeasureMode,- const YGMeasureMode heightMeasureMode,- const float parentWidth,- const float parentHeight) {- const float paddingAndBorderAxisRow =- YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, parentWidth);- const float paddingAndBorderAxisColumn =- YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, parentWidth);- const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth);- const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth);-- node->layout.measuredDimensions[YGDimensionWidth] =- YGNodeBoundAxis(node,- YGFlexDirectionRow,- (widthMeasureMode == YGMeasureModeUndefined ||- widthMeasureMode == YGMeasureModeAtMost)- ? paddingAndBorderAxisRow- : availableWidth - marginAxisRow,- parentWidth,- parentWidth);- node->layout.measuredDimensions[YGDimensionHeight] =- YGNodeBoundAxis(node,- YGFlexDirectionColumn,- (heightMeasureMode == YGMeasureModeUndefined ||- heightMeasureMode == YGMeasureModeAtMost)- ? paddingAndBorderAxisColumn- : availableHeight - marginAxisColumn,- parentHeight,- parentWidth);-}--static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGMeasureMode widthMeasureMode,- const YGMeasureMode heightMeasureMode,- const float parentWidth,- const float parentHeight) {- if ((widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) ||- (heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) ||- (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly)) {- const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth);- const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth);-- node->layout.measuredDimensions[YGDimensionWidth] =- YGNodeBoundAxis(node,- YGFlexDirectionRow,- YGFloatIsUndefined(availableWidth) ||- (widthMeasureMode == YGMeasureModeAtMost && availableWidth < 0.0f)- ? 0.0f- : availableWidth - marginAxisRow,- parentWidth,- parentWidth);-- node->layout.measuredDimensions[YGDimensionHeight] =- YGNodeBoundAxis(node,- YGFlexDirectionColumn,- YGFloatIsUndefined(availableHeight) ||- (heightMeasureMode == YGMeasureModeAtMost && availableHeight < 0.0f)- ? 0.0f- : availableHeight - marginAxisColumn,- parentHeight,- parentWidth);-- return true;- }-- return false;-}--static void YGZeroOutLayoutRecursivly(const YGNodeRef node) {- node->layout.dimensions[YGDimensionHeight] = 0;- node->layout.dimensions[YGDimensionWidth] = 0;- node->layout.position[YGEdgeTop] = 0;- node->layout.position[YGEdgeBottom] = 0;- node->layout.position[YGEdgeLeft] = 0;- node->layout.position[YGEdgeRight] = 0;- node->layout.cachedLayout.availableHeight = 0;- node->layout.cachedLayout.availableWidth = 0;- node->layout.cachedLayout.heightMeasureMode = YGMeasureModeExactly;- node->layout.cachedLayout.widthMeasureMode = YGMeasureModeExactly;- node->layout.cachedLayout.computedWidth = 0;- node->layout.cachedLayout.computedHeight = 0;- node->hasNewLayout = true;- const uint32_t childCount = YGNodeGetChildCount(node);- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- YGZeroOutLayoutRecursivly(child);- }-}--//-// This is the main routine that implements a subset of the flexbox layout-// algorithm-// described in the W3C YG documentation: https://www.w3.org/TR/YG3-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.-// * 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-// or YGUndefined if the size is not available; interpretation depends on-// layout-// 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-// 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-// 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/YG3-sizing/#terms):-// - YGMeasureModeUndefined: max content-// - YGMeasureModeExactly: fill available-// - YGMeasureModeAtMost: fit content-//-// When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller passes-// an available size of-// undefined then it must also pass a measure mode of YGMeasureModeUndefined-// in that dimension.-//-static void YGNodelayoutImpl(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGDirection parentDirection,- const YGMeasureMode widthMeasureMode,- const YGMeasureMode heightMeasureMode,- const float parentWidth,- const float parentHeight,- const bool performLayout,- const YGConfigRef config) {- YG_ASSERT(YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined : true,- "availableWidth is indefinite so widthMeasureMode must be "- "YGMeasureModeUndefined");- YG_ASSERT(YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined- : true,- "availableHeight is indefinite so heightMeasureMode must be "- "YGMeasureModeUndefined");-- // Set the resolved resolution in the node's layout.- const YGDirection direction = YGNodeResolveDirection(node, parentDirection);- node->layout.direction = direction;-- const YGFlexDirection flexRowDirection = YGResolveFlexDirection(YGFlexDirectionRow, direction);- const YGFlexDirection flexColumnDirection =- YGResolveFlexDirection(YGFlexDirectionColumn, direction);-- node->layout.margin[YGEdgeStart] = YGNodeLeadingMargin(node, flexRowDirection, parentWidth);- node->layout.margin[YGEdgeEnd] = YGNodeTrailingMargin(node, flexRowDirection, parentWidth);- node->layout.margin[YGEdgeTop] = YGNodeLeadingMargin(node, flexColumnDirection, parentWidth);- node->layout.margin[YGEdgeBottom] = YGNodeTrailingMargin(node, flexColumnDirection, parentWidth);-- node->layout.border[YGEdgeStart] = YGNodeLeadingBorder(node, flexRowDirection);- node->layout.border[YGEdgeEnd] = YGNodeTrailingBorder(node, flexRowDirection);- node->layout.border[YGEdgeTop] = YGNodeLeadingBorder(node, flexColumnDirection);- node->layout.border[YGEdgeBottom] = YGNodeTrailingBorder(node, flexColumnDirection);-- node->layout.padding[YGEdgeStart] = YGNodeLeadingPadding(node, flexRowDirection, parentWidth);- node->layout.padding[YGEdgeEnd] = YGNodeTrailingPadding(node, flexRowDirection, parentWidth);- node->layout.padding[YGEdgeTop] = YGNodeLeadingPadding(node, flexColumnDirection, parentWidth);- node->layout.padding[YGEdgeBottom] =- YGNodeTrailingPadding(node, flexColumnDirection, parentWidth);-- if (node->measure) {- YGNodeWithMeasureFuncSetMeasuredDimensions(node,- availableWidth,- availableHeight,- widthMeasureMode,- heightMeasureMode,- parentWidth,- parentHeight);- return;- }-- const uint32_t childCount = YGNodeListCount(node->children);- if (childCount == 0) {- YGNodeEmptyContainerSetMeasuredDimensions(node,- availableWidth,- availableHeight,- widthMeasureMode,- heightMeasureMode,- parentWidth,- parentHeight);- return;- }-- // If we're not being asked to perform a full layout we can skip the algorithm if we already know- // the size- if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions(node,- availableWidth,- availableHeight,- widthMeasureMode,- heightMeasureMode,- parentWidth,- parentHeight)) {- return;- }-- // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM- const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction);- const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);- const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);- const YGJustify justifyContent = node->style.justifyContent;- const bool isNodeFlexWrap = node->style.flexWrap != YGWrapNoWrap;-- const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight;- const float crossAxisParentSize = isMainAxisRow ? parentHeight : parentWidth;-- YGNodeRef firstAbsoluteChild = NULL;- YGNodeRef currentAbsoluteChild = NULL;-- const float leadingPaddingAndBorderMain =- YGNodeLeadingPaddingAndBorder(node, mainAxis, parentWidth);- const float trailingPaddingAndBorderMain =- YGNodeTrailingPaddingAndBorder(node, mainAxis, parentWidth);- const float leadingPaddingAndBorderCross =- YGNodeLeadingPaddingAndBorder(node, crossAxis, parentWidth);- const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, parentWidth);- const float paddingAndBorderAxisCross =- YGNodePaddingAndBorderForAxis(node, crossAxis, parentWidth);-- YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;- YGMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;-- const float paddingAndBorderAxisRow =- isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross;- const float paddingAndBorderAxisColumn =- isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain;-- const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth);- const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth);-- // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS- const float minInnerWidth =- YGResolveValue(&node->style.minDimensions[YGDimensionWidth], parentWidth) - marginAxisRow -- paddingAndBorderAxisRow;- const float maxInnerWidth =- YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth) - marginAxisRow -- paddingAndBorderAxisRow;- const float minInnerHeight =- YGResolveValue(&node->style.minDimensions[YGDimensionHeight], parentHeight) -- marginAxisColumn - paddingAndBorderAxisColumn;- const float maxInnerHeight =- YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight) -- marginAxisColumn - paddingAndBorderAxisColumn;- const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight;- const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight;-- // Max dimension overrides predefined dimension value; Min dimension in turn overrides both of the- // above- float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;- if (!YGFloatIsUndefined(availableInnerWidth)) {- // We want to make sure our available width does not violate min and max constraints- availableInnerWidth = fmaxf(fminf(availableInnerWidth, maxInnerWidth), minInnerWidth);- }-- float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;- if (!YGFloatIsUndefined(availableInnerHeight)) {- // We want to make sure our available height does not violate min and max constraints- availableInnerHeight = fmaxf(fminf(availableInnerHeight, maxInnerHeight), minInnerHeight);- }-- float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;- const float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;-- // If there is only one child with flexGrow + flexShrink it means we can set the- // computedFlexBasis to 0 instead of measuring and shrinking / flexing the child to exactly- // match the remaining space- YGNodeRef singleFlexChild = NULL;- if (measureModeMainDim == YGMeasureModeExactly) {- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeGetChild(node, i);- if (singleFlexChild) {- if (YGNodeIsFlex(child)) {- // There is already a flexible child, abort.- singleFlexChild = NULL;- break;- }- } else if (YGResolveFlexGrow(child) > 0.0f && YGNodeResolveFlexShrink(child) > 0.0f) {- singleFlexChild = child;- }- }- }-- float totalFlexBasis = 0;-- // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- if (child->style.display == YGDisplayNone) {- YGZeroOutLayoutRecursivly(child);- child->hasNewLayout = true;- child->isDirty = false;- continue;- }- YGResolveDimensions(child);- if (performLayout) {- // Set the initial position (relative to the parent).- const YGDirection childDirection = YGNodeResolveDirection(child, direction);- YGNodeSetPosition(child,- childDirection,- availableInnerMainDim,- availableInnerCrossDim,- availableInnerWidth);- }-- // Absolute-positioned children don't participate in flex layout. Add them- // to a list that we can process later.- if (child->style.positionType == YGPositionTypeAbsolute) {- // Store a private linked list of absolutely positioned children- // so that we can efficiently traverse them later.- if (firstAbsoluteChild == NULL) {- firstAbsoluteChild = child;- }- if (currentAbsoluteChild != NULL) {- currentAbsoluteChild->nextChild = child;- }- currentAbsoluteChild = child;- child->nextChild = NULL;- } else {- if (child == singleFlexChild) {- child->layout.computedFlexBasisGeneration = gCurrentGenerationCount;- child->layout.computedFlexBasis = 0;- } else {- YGNodeComputeFlexBasisForChild(node,- child,- availableInnerWidth,- widthMeasureMode,- availableInnerHeight,- availableInnerWidth,- availableInnerHeight,- heightMeasureMode,- direction,- config);- }- }-- totalFlexBasis += child->layout.computedFlexBasis;- }-- const bool flexBasisOverflows =- measureModeMainDim == YGMeasureModeUndefined ? false : totalFlexBasis > availableInnerMainDim;- if (isNodeFlexWrap && flexBasisOverflows && measureModeMainDim == YGMeasureModeAtMost) {- measureModeMainDim = YGMeasureModeExactly;- }-- // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES-- // Indexes of children that represent the first and last items in the line.- uint32_t startOfLineIndex = 0;- uint32_t endOfLineIndex = 0;-- // Number of lines.- uint32_t lineCount = 0;-- // Accumulated cross dimensions of all lines so far.- float totalLineCrossDim = 0;-- // Max main dimension of all the lines.- float maxLineMainDim = 0;-- for (; endOfLineIndex < childCount; lineCount++, startOfLineIndex = endOfLineIndex) {- // 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.- uint32_t 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;-- // Maintain a linked list of the child nodes that can shrink and/or grow.- YGNodeRef firstRelativeChild = NULL;- YGNodeRef currentRelativeChild = NULL;-- // Add items to the current line until it's full or we run out of items.- for (uint32_t i = startOfLineIndex; i < childCount; i++, endOfLineIndex++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- if (child->style.display == YGDisplayNone) {- continue;- }- child->lineIndex = lineCount;-- if (child->style.positionType != YGPositionTypeAbsolute) {- const float outerFlexBasis =- fmaxf(YGResolveValue(&child->style.minDimensions[dim[mainAxis]], mainAxisParentSize),- child->layout.computedFlexBasis) +- YGNodeMarginForAxis(child, mainAxis, availableInnerWidth);-- // 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 (YGNodeIsFlex(child)) {- totalFlexGrowFactors += YGResolveFlexGrow(child);-- // Unlike the grow factor, the shrink factor is scaled relative to the- // child- // dimension.- totalFlexShrinkScaledFactors +=- -YGNodeResolveFlexShrink(child) * child->layout.computedFlexBasis;- }-- // Store a private linked list of children that need to be layed out.- if (firstRelativeChild == NULL) {- firstRelativeChild = child;- }- if (currentRelativeChild != NULL) {- currentRelativeChild->nextChild = child;- }- currentRelativeChild = child;- child->nextChild = NULL;- }- }-- // If we don't need to measure the cross axis, we can skip the entire flex- // step.- const bool canSkipFlex = !performLayout && measureModeCrossDim == YGMeasureModeExactly;-- // 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.-- // If we don't measure with exact main dimension we want to ensure we don't violate min and max- if (measureModeMainDim != YGMeasureModeExactly) {- if (!YGFloatIsUndefined(minInnerMainDim) && sizeConsumedOnCurrentLine < minInnerMainDim) {- availableInnerMainDim = minInnerMainDim;- } else if (!YGFloatIsUndefined(maxInnerMainDim) && sizeConsumedOnCurrentLine > maxInnerMainDim) {- availableInnerMainDim = maxInnerMainDim;- } else if (YGConfigIsExperimentalFeatureEnabled(node->config, YGExperimentalFeatureMinFlexFix)) {- // TODO: this needs to be moved out of experimental feature, as this is legitimate fix- // If the measurement isn't exact, we want to use as little space as possible- availableInnerMainDim = sizeConsumedOnCurrentLine;- }- }-- float remainingFreeSpace = 0;- if (!YGFloatIsUndefined(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 points for- // its content. Consequently, remainingFreeSpace is 0 -- // sizeConsumedOnCurrentLine.- remainingFreeSpace = -sizeConsumedOnCurrentLine;- }-- const 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/YG-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) {- childFlexBasis = currentRelativeChild->layout.computedFlexBasis;-- if (remainingFreeSpace < 0) {- flexShrinkScaledFactor = -YGNodeResolveFlexShrink(currentRelativeChild) * childFlexBasis;-- // Is this child able to shrink?- if (flexShrinkScaledFactor != 0) {- baseMainSize =- childFlexBasis +- remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;- boundMainSize = YGNodeBoundAxis(currentRelativeChild,- mainAxis,- baseMainSize,- availableInnerMainDim,- availableInnerWidth);- 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 = YGResolveFlexGrow(currentRelativeChild);-- // Is this child able to grow?- if (flexGrowFactor != 0) {- baseMainSize =- childFlexBasis + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;- boundMainSize = YGNodeBoundAxis(currentRelativeChild,- mainAxis,- baseMainSize,- availableInnerMainDim,- availableInnerWidth);- 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;- }- }- }-- currentRelativeChild = currentRelativeChild->nextChild;- }-- totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;- totalFlexGrowFactors += deltaFlexGrowFactors;- remainingFreeSpace += deltaFreeSpace;-- // Second pass: resolve the sizes of the flexible items- deltaFreeSpace = 0;- currentRelativeChild = firstRelativeChild;- while (currentRelativeChild != NULL) {- childFlexBasis = currentRelativeChild->layout.computedFlexBasis;- float updatedMainSize = childFlexBasis;-- if (remainingFreeSpace < 0) {- flexShrinkScaledFactor = -YGNodeResolveFlexShrink(currentRelativeChild) * childFlexBasis;- // Is this child able to shrink?- if (flexShrinkScaledFactor != 0) {- float childSize;-- if (totalFlexShrinkScaledFactors == 0) {- childSize = childFlexBasis + flexShrinkScaledFactor;- } else {- childSize =- childFlexBasis +- (remainingFreeSpace / totalFlexShrinkScaledFactors) * flexShrinkScaledFactor;- }-- updatedMainSize = YGNodeBoundAxis(currentRelativeChild,- mainAxis,- childSize,- availableInnerMainDim,- availableInnerWidth);- }- } else if (remainingFreeSpace > 0) {- flexGrowFactor = YGResolveFlexGrow(currentRelativeChild);-- // Is this child able to grow?- if (flexGrowFactor != 0) {- updatedMainSize =- YGNodeBoundAxis(currentRelativeChild,- mainAxis,- childFlexBasis +- remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor,- availableInnerMainDim,- availableInnerWidth);- }- }-- deltaFreeSpace -= updatedMainSize - childFlexBasis;-- const float marginMain =- YGNodeMarginForAxis(currentRelativeChild, mainAxis, availableInnerWidth);- const float marginCross =- YGNodeMarginForAxis(currentRelativeChild, crossAxis, availableInnerWidth);-- float childCrossSize;- float childMainSize = updatedMainSize + marginMain;- YGMeasureMode childCrossMeasureMode;- YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;-- if (!YGFloatIsUndefined(availableInnerCrossDim) &&- !YGNodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) &&- measureModeCrossDim == YGMeasureModeExactly &&- !(isNodeFlexWrap && flexBasisOverflows) &&- YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch) {- childCrossSize = availableInnerCrossDim;- childCrossMeasureMode = YGMeasureModeExactly;- } else if (!YGNodeIsStyleDimDefined(currentRelativeChild,- crossAxis,- availableInnerCrossDim)) {- childCrossSize = availableInnerCrossDim;- childCrossMeasureMode =- YGFloatIsUndefined(childCrossSize) ? YGMeasureModeUndefined : YGMeasureModeAtMost;- } else {- childCrossSize = YGResolveValue(currentRelativeChild->resolvedDimensions[dim[crossAxis]],- availableInnerCrossDim) +- marginCross;- const bool isLoosePercentageMeasurement =- currentRelativeChild->resolvedDimensions[dim[crossAxis]]->unit == YGUnitPercent &&- measureModeCrossDim != YGMeasureModeExactly;- childCrossMeasureMode = YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement- ? YGMeasureModeUndefined- : YGMeasureModeExactly;- }-- if (!YGFloatIsUndefined(currentRelativeChild->style.aspectRatio)) {- childCrossSize = fmaxf(- isMainAxisRow- ? (childMainSize - marginMain) / currentRelativeChild->style.aspectRatio- : (childMainSize - marginMain) * currentRelativeChild->style.aspectRatio,- YGNodePaddingAndBorderForAxis(currentRelativeChild, crossAxis, availableInnerWidth));- childCrossMeasureMode = YGMeasureModeExactly;-- // Parent size constraint should have higher priority than flex- if (YGNodeIsFlex(currentRelativeChild)) {- childCrossSize = fminf(childCrossSize - marginCross, availableInnerCrossDim);- childMainSize =- marginMain + (isMainAxisRow- ? childCrossSize * currentRelativeChild->style.aspectRatio- : childCrossSize / currentRelativeChild->style.aspectRatio);- }-- childCrossSize += marginCross;- }-- YGConstrainMaxSizeForMode(currentRelativeChild,- mainAxis,- availableInnerMainDim,- availableInnerWidth,- &childMainMeasureMode,- &childMainSize);- YGConstrainMaxSizeForMode(currentRelativeChild,- crossAxis,- availableInnerCrossDim,- availableInnerWidth,- &childCrossMeasureMode,- &childCrossSize);-- const bool requiresStretchLayout =- !YGNodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) &&- YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch;-- const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;- const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;-- const YGMeasureMode childWidthMeasureMode =- isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;- const YGMeasureMode childHeightMeasureMode =- !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;-- // Recursively call the layout algorithm for this child with the updated- // main size.- YGLayoutNodeInternal(currentRelativeChild,- childWidth,- childHeight,- direction,- childWidthMeasureMode,- childHeightMeasureMode,- availableInnerWidth,- availableInnerHeight,- performLayout && !requiresStretchLayout,- "flex",- config);-- currentRelativeChild = currentRelativeChild->nextChild;- }- }-- 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. Calculate the remaining space when- // constraint by the min size defined for the main axis.-- if (measureModeMainDim == YGMeasureModeAtMost && remainingFreeSpace > 0) {- if (node->style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined &&- YGResolveValue(&node->style.minDimensions[dim[mainAxis]], mainAxisParentSize) >= 0) {- remainingFreeSpace =- fmaxf(0,- YGResolveValue(&node->style.minDimensions[dim[mainAxis]], mainAxisParentSize) -- (availableInnerMainDim - remainingFreeSpace));- } else {- remainingFreeSpace = 0;- }- }-- int numberOfAutoMarginsOnCurrentLine = 0;- for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- if (child->style.positionType == YGPositionTypeRelative) {- if (YGMarginLeadingValue(child, mainAxis)->unit == YGUnitAuto) {- numberOfAutoMarginsOnCurrentLine++;- }- if (YGMarginTrailingValue(child, mainAxis)->unit == YGUnitAuto) {- numberOfAutoMarginsOnCurrentLine++;- }- }- }-- if (numberOfAutoMarginsOnCurrentLine == 0) {- switch (justifyContent) {- case YGJustifyCenter:- leadingMainDim = remainingFreeSpace / 2;- break;- case YGJustifyFlexEnd:- leadingMainDim = remainingFreeSpace;- break;- case YGJustifySpaceBetween:- if (itemsOnLine > 1) {- betweenMainDim = fmaxf(remainingFreeSpace, 0) / (itemsOnLine - 1);- } else {- betweenMainDim = 0;- }- break;- case YGJustifySpaceAround:- // Space on the edges is half of the space between elements- betweenMainDim = remainingFreeSpace / itemsOnLine;- leadingMainDim = betweenMainDim / 2;- break;- case YGJustifyFlexStart:- break;- }- }-- float mainDim = leadingPaddingAndBorderMain + leadingMainDim;- float crossDim = 0;-- for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- if (child->style.display == YGDisplayNone) {- continue;- }- if (child->style.positionType == YGPositionTypeAbsolute &&- YGNodeIsLeadingPosDefined(child, 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]] =- YGNodeLeadingPosition(child, mainAxis, availableInnerMainDim) +- YGNodeLeadingBorder(node, mainAxis) +- YGNodeLeadingMargin(child, mainAxis, availableInnerWidth);- }- } else {- // 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.- if (child->style.positionType == YGPositionTypeRelative) {- if (YGMarginLeadingValue(child, mainAxis)->unit == YGUnitAuto) {- mainDim += remainingFreeSpace / numberOfAutoMarginsOnCurrentLine;- }-- if (performLayout) {- child->layout.position[pos[mainAxis]] += mainDim;- }-- if (YGMarginTrailingValue(child, mainAxis)->unit == YGUnitAuto) {- mainDim += remainingFreeSpace / numberOfAutoMarginsOnCurrentLine;- }-- 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 YGNodeDimWithMargin.- mainDim += betweenMainDim + YGNodeMarginForAxis(child, mainAxis, availableInnerWidth) +- child->layout.computedFlexBasis;- crossDim = availableInnerCrossDim;- } else {- // The main dimension is the sum of all the elements dimension plus the spacing.- mainDim += betweenMainDim + YGNodeDimWithMargin(child, mainAxis, availableInnerWidth);-- // The cross dimension is the max of the elements dimension since- // there can only be one element in that cross dimension.- crossDim = fmaxf(crossDim, YGNodeDimWithMargin(child, crossAxis, availableInnerWidth));- }- } else if (performLayout) {- child->layout.position[pos[mainAxis]] +=- YGNodeLeadingBorder(node, mainAxis) + leadingMainDim;- }- }- }-- mainDim += trailingPaddingAndBorderMain;-- float containerCrossAxis = availableInnerCrossDim;- if (measureModeCrossDim == YGMeasureModeUndefined ||- measureModeCrossDim == YGMeasureModeAtMost) {- // Compute the cross axis from the max cross dimension of the children.- containerCrossAxis = YGNodeBoundAxis(node,- crossAxis,- crossDim + paddingAndBorderAxisCross,- crossAxisParentSize,- parentWidth) -- paddingAndBorderAxisCross;- }-- // If there's no flex wrap, the cross dimension is defined by the container.- if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) {- crossDim = availableInnerCrossDim;- }-- // Clamp to the min/max size specified on the container.- crossDim = YGNodeBoundAxis(node,- crossAxis,- crossDim + paddingAndBorderAxisCross,- crossAxisParentSize,- parentWidth) -- paddingAndBorderAxisCross;-- // STEP 7: CROSS-AXIS ALIGNMENT- // We can skip child alignment if we're just measuring the container.- if (performLayout) {- for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- if (child->style.display == YGDisplayNone) {- continue;- }- if (child->style.positionType == YGPositionTypeAbsolute) {- // 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 (YGNodeIsLeadingPosDefined(child, crossAxis)) {- child->layout.position[pos[crossAxis]] =- YGNodeLeadingPosition(child, crossAxis, availableInnerCrossDim) +- YGNodeLeadingBorder(node, crossAxis) +- YGNodeLeadingMargin(child, crossAxis, availableInnerWidth);- } else {- child->layout.position[pos[crossAxis]] =- YGNodeLeadingBorder(node, crossAxis) +- YGNodeLeadingMargin(child, crossAxis, availableInnerWidth);- }- } 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- const YGAlign alignItem = YGNodeAlignItem(node, child);-- // 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.- if (alignItem == YGAlignStretch &&- YGMarginLeadingValue(child, crossAxis)->unit != YGUnitAuto &&- YGMarginTrailingValue(child, crossAxis)->unit != YGUnitAuto) {- // If the child defines a definite size for its cross axis, there's- // no need to stretch.- if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) {- float childMainSize = child->layout.measuredDimensions[dim[mainAxis]];- float childCrossSize =- !YGFloatIsUndefined(child->style.aspectRatio)- ? ((YGNodeMarginForAxis(child, crossAxis, availableInnerWidth) +- (isMainAxisRow ? childMainSize / child->style.aspectRatio- : childMainSize * child->style.aspectRatio)))- : crossDim;-- childMainSize += YGNodeMarginForAxis(child, mainAxis, availableInnerWidth);-- YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;- YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly;- YGConstrainMaxSizeForMode(child,- mainAxis,- availableInnerMainDim,- availableInnerWidth,- &childMainMeasureMode,- &childMainSize);- YGConstrainMaxSizeForMode(child,- crossAxis,- availableInnerCrossDim,- availableInnerWidth,- &childCrossMeasureMode,- &childCrossSize);-- const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;- const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;-- const YGMeasureMode childWidthMeasureMode =- YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly;- const YGMeasureMode childHeightMeasureMode =- YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly;-- YGLayoutNodeInternal(child,- childWidth,- childHeight,- direction,- childWidthMeasureMode,- childHeightMeasureMode,- availableInnerWidth,- availableInnerHeight,- true,- "stretch",- config);- }- } else {- const float remainingCrossDim =- containerCrossAxis - YGNodeDimWithMargin(child, crossAxis, availableInnerWidth);-- if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto &&- YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) {- leadingCrossDim += fmaxf(0.0f, remainingCrossDim / 2);- } else if (YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) {- // No-Op- } else if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto) {- leadingCrossDim += fmaxf(0.0f, remainingCrossDim);- } else if (alignItem == YGAlignFlexStart) {- // No-Op- } else if (alignItem == YGAlignCenter) {- leadingCrossDim += remainingCrossDim / 2;- } else {- leadingCrossDim += remainingCrossDim;- }- }- // And we apply the position- child->layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;- }- }- }-- totalLineCrossDim += crossDim;- maxLineMainDim = fmaxf(maxLineMainDim, mainDim);- }-- // STEP 8: MULTI-LINE CONTENT ALIGNMENT- if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) &&- !YGFloatIsUndefined(availableInnerCrossDim)) {- const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;-- float crossDimLead = 0;- float currentLead = leadingPaddingAndBorderCross;-- switch (node->style.alignContent) {- case YGAlignFlexEnd:- currentLead += remainingAlignContentDim;- break;- case YGAlignCenter:- currentLead += remainingAlignContentDim / 2;- break;- case YGAlignStretch:- if (availableInnerCrossDim > totalLineCrossDim) {- crossDimLead = remainingAlignContentDim / lineCount;- }- break;- case YGAlignSpaceAround:- if (availableInnerCrossDim > totalLineCrossDim) {- currentLead += remainingAlignContentDim / (2 * lineCount);- if (lineCount > 1) {- crossDimLead = remainingAlignContentDim / lineCount;- }- } else {- currentLead += remainingAlignContentDim / 2;- }- break;- case YGAlignSpaceBetween:- if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) {- crossDimLead = remainingAlignContentDim / (lineCount - 1);- }- break;- case YGAlignAuto:- case YGAlignFlexStart:- case YGAlignBaseline:- break;- }-- uint32_t endIndex = 0;- for (uint32_t i = 0; i < lineCount; i++) {- const uint32_t startIndex = endIndex;- uint32_t ii;-- // compute the line's height and find the endIndex- float lineHeight = 0;- float maxAscentForCurrentLine = 0;- float maxDescentForCurrentLine = 0;- for (ii = startIndex; ii < childCount; ii++) {- const YGNodeRef child = YGNodeListGet(node->children, ii);- if (child->style.display == YGDisplayNone) {- continue;- }- if (child->style.positionType == YGPositionTypeRelative) {- if (child->lineIndex != i) {- break;- }- if (YGNodeIsLayoutDimDefined(child, crossAxis)) {- lineHeight = fmaxf(lineHeight,- child->layout.measuredDimensions[dim[crossAxis]] +- YGNodeMarginForAxis(child, crossAxis, availableInnerWidth));- }- if (YGNodeAlignItem(node, child) == YGAlignBaseline) {- const float ascent =- YGBaseline(child) +- YGNodeLeadingMargin(child, YGFlexDirectionColumn, availableInnerWidth);- const float descent =- child->layout.measuredDimensions[YGDimensionHeight] +- YGNodeMarginForAxis(child, YGFlexDirectionColumn, availableInnerWidth) - ascent;- maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent);- maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent);- lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine);- }- }- }- endIndex = ii;- lineHeight += crossDimLead;-- if (performLayout) {- for (ii = startIndex; ii < endIndex; ii++) {- const YGNodeRef child = YGNodeListGet(node->children, ii);- if (child->style.display == YGDisplayNone) {- continue;- }- if (child->style.positionType == YGPositionTypeRelative) {- switch (YGNodeAlignItem(node, child)) {- case YGAlignFlexStart: {- child->layout.position[pos[crossAxis]] =- currentLead + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth);- break;- }- case YGAlignFlexEnd: {- child->layout.position[pos[crossAxis]] =- currentLead + lineHeight -- YGNodeTrailingMargin(child, crossAxis, availableInnerWidth) -- child->layout.measuredDimensions[dim[crossAxis]];- break;- }- case YGAlignCenter: {- float childHeight = child->layout.measuredDimensions[dim[crossAxis]];- child->layout.position[pos[crossAxis]] =- currentLead + (lineHeight - childHeight) / 2;- break;- }- case YGAlignStretch: {- child->layout.position[pos[crossAxis]] =- currentLead + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth);-- // Remeasure child with the line height as it as been only measured with the- // parents height yet.- if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) {- const float childWidth =- isMainAxisRow ? (child->layout.measuredDimensions[YGDimensionWidth] +- YGNodeMarginForAxis(child, crossAxis, availableInnerWidth))- : lineHeight;-- const float childHeight =- !isMainAxisRow ? (child->layout.measuredDimensions[YGDimensionHeight] +- YGNodeMarginForAxis(child, crossAxis, availableInnerWidth))- : lineHeight;-- if (!(YGFloatsEqual(childWidth,- child->layout.measuredDimensions[YGDimensionWidth]) &&- YGFloatsEqual(childHeight,- child->layout.measuredDimensions[YGDimensionHeight]))) {- YGLayoutNodeInternal(child,- childWidth,- childHeight,- direction,- YGMeasureModeExactly,- YGMeasureModeExactly,- availableInnerWidth,- availableInnerHeight,- true,- "multiline-stretch",- config);- }- }- break;- }- case YGAlignBaseline: {- child->layout.position[YGEdgeTop] =- currentLead + maxAscentForCurrentLine - YGBaseline(child) +- YGNodeLeadingPosition(child, YGFlexDirectionColumn, availableInnerCrossDim);- break;- }- case YGAlignAuto:- case YGAlignSpaceBetween:- case YGAlignSpaceAround:- break;- }- }- }- }-- currentLead += lineHeight;- }- }-- // STEP 9: COMPUTING FINAL DIMENSIONS- node->layout.measuredDimensions[YGDimensionWidth] = YGNodeBoundAxis(- node, YGFlexDirectionRow, availableWidth - marginAxisRow, parentWidth, parentWidth);- node->layout.measuredDimensions[YGDimensionHeight] = YGNodeBoundAxis(- node, YGFlexDirectionColumn, availableHeight - marginAxisColumn, parentHeight, parentWidth);-- // If the user didn't specify a width or height for the node, set the- // dimensions based on the children.- if (measureModeMainDim == YGMeasureModeUndefined ||- (node->style.overflow != YGOverflowScroll && measureModeMainDim == YGMeasureModeAtMost)) {- // Clamp the size to the min/max size, if specified, and make sure it- // doesn't go below the padding and border amount.- node->layout.measuredDimensions[dim[mainAxis]] =- YGNodeBoundAxis(node, mainAxis, maxLineMainDim, mainAxisParentSize, parentWidth);- } else if (measureModeMainDim == YGMeasureModeAtMost &&- node->style.overflow == YGOverflowScroll) {- node->layout.measuredDimensions[dim[mainAxis]] = fmaxf(- fminf(availableInnerMainDim + paddingAndBorderAxisMain,- YGNodeBoundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim, mainAxisParentSize)),- paddingAndBorderAxisMain);- }-- if (measureModeCrossDim == YGMeasureModeUndefined ||- (node->style.overflow != YGOverflowScroll && measureModeCrossDim == YGMeasureModeAtMost)) {- // Clamp the size to the min/max size, if specified, and make sure it- // doesn't go below the padding and border amount.- node->layout.measuredDimensions[dim[crossAxis]] =- YGNodeBoundAxis(node,- crossAxis,- totalLineCrossDim + paddingAndBorderAxisCross,- crossAxisParentSize,- parentWidth);- } else if (measureModeCrossDim == YGMeasureModeAtMost &&- node->style.overflow == YGOverflowScroll) {- node->layout.measuredDimensions[dim[crossAxis]] =- fmaxf(fminf(availableInnerCrossDim + paddingAndBorderAxisCross,- YGNodeBoundAxisWithinMinAndMax(node,- crossAxis,- totalLineCrossDim + paddingAndBorderAxisCross,- crossAxisParentSize)),- paddingAndBorderAxisCross);- }-- // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse.- if (performLayout && node->style.flexWrap == YGWrapWrapReverse) {- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeGetChild(node, i);- if (child->style.positionType == YGPositionTypeRelative) {- child->layout.position[pos[crossAxis]] = node->layout.measuredDimensions[dim[crossAxis]] -- child->layout.position[pos[crossAxis]] -- child->layout.measuredDimensions[dim[crossAxis]];- }- }- }-- if (performLayout) {- // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN- for (currentAbsoluteChild = firstAbsoluteChild; currentAbsoluteChild != NULL;- currentAbsoluteChild = currentAbsoluteChild->nextChild) {- YGNodeAbsoluteLayoutChild(node,- currentAbsoluteChild,- availableInnerWidth,- isMainAxisRow ? measureModeMainDim : measureModeCrossDim,- availableInnerHeight,- direction,- config);- }-- // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN- const bool needsMainTrailingPos =- mainAxis == YGFlexDirectionRowReverse || mainAxis == YGFlexDirectionColumnReverse;- const bool needsCrossTrailingPos =- crossAxis == YGFlexDirectionRowReverse || crossAxis == YGFlexDirectionColumnReverse;-- // Set trailing position if necessary.- if (needsMainTrailingPos || needsCrossTrailingPos) {- for (uint32_t i = 0; i < childCount; i++) {- const YGNodeRef child = YGNodeListGet(node->children, i);- if (child->style.display == YGDisplayNone) {- continue;- }- if (needsMainTrailingPos) {- YGNodeSetChildTrailingPosition(node, child, mainAxis);- }-- if (needsCrossTrailingPos) {- YGNodeSetChildTrailingPosition(node, child, crossAxis);- }- }- }- }-}--uint32_t gDepth = 0;-bool gPrintTree = false;-bool gPrintChanges = false;-bool gPrintSkips = false;--static const char *spacer = " ";--static const char *YGSpacer(const unsigned long level) {- const size_t spacerLen = strlen(spacer);- if (level > spacerLen) {- return &spacer[0];- } else {- return &spacer[spacerLen - level];- }-}--static const char *YGMeasureModeName(const YGMeasureMode mode, const bool performLayout) {- const char *kMeasureModeNames[YGMeasureModeCount] = {"UNDEFINED", "EXACTLY", "AT_MOST"};- const char *kLayoutModeNames[YGMeasureModeCount] = {"LAY_UNDEFINED",- "LAY_EXACTLY",- "LAY_AT_"- "MOST"};-- if (mode >= YGMeasureModeCount) {- return "";- }-- return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode];-}--static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(YGMeasureMode sizeMode,- float size,- float lastComputedSize) {- return sizeMode == YGMeasureModeExactly && YGFloatsEqual(size, lastComputedSize);-}--static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(YGMeasureMode sizeMode,- float size,- YGMeasureMode lastSizeMode,- float lastComputedSize) {- return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined &&- (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize));-}--static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureMode sizeMode,- float size,- YGMeasureMode lastSizeMode,- float lastSize,- float lastComputedSize) {- return lastSizeMode == YGMeasureModeAtMost && sizeMode == YGMeasureModeAtMost &&- lastSize > size && (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize));-}--bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode,- const float width,- const YGMeasureMode heightMode,- const float height,- const YGMeasureMode lastWidthMode,- const float lastWidth,- const YGMeasureMode lastHeightMode,- const float lastHeight,- const float lastComputedWidth,- const float lastComputedHeight,- const float marginRow,- const float marginColumn) {- if (lastComputedHeight < 0 || lastComputedWidth < 0) {- return false;- }-- const bool hasSameWidthSpec = lastWidthMode == widthMode && YGFloatsEqual(lastWidth, width);- const bool hasSameHeightSpec = lastHeightMode == heightMode && YGFloatsEqual(lastHeight, height);-- const bool widthIsCompatible =- hasSameWidthSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode,- width - marginRow,- lastComputedWidth) ||- YGMeasureModeOldSizeIsUnspecifiedAndStillFits(widthMode,- width - marginRow,- lastWidthMode,- lastComputedWidth) ||- YGMeasureModeNewMeasureSizeIsStricterAndStillValid(- widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth);-- const bool heightIsCompatible =- hasSameHeightSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode,- height - marginColumn,- lastComputedHeight) ||- YGMeasureModeOldSizeIsUnspecifiedAndStillFits(heightMode,- height - marginColumn,- lastHeightMode,- lastComputedHeight) ||- YGMeasureModeNewMeasureSizeIsStricterAndStillValid(- heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight);-- return widthIsCompatible && heightIsCompatible;-}--//-// This is a wrapper around the YGNodelayoutImpl function. It determines-// whether the layout request is redundant and can be skipped.-//-// Parameters:-// Input parameters are the same as YGNodelayoutImpl (see above)-// Return parameter is true if layout was performed, false if skipped-//-bool YGLayoutNodeInternal(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGDirection parentDirection,- const YGMeasureMode widthMeasureMode,- const YGMeasureMode heightMeasureMode,- const float parentWidth,- const float parentHeight,- const bool performLayout,- const char *reason,- const YGConfigRef config) {- YGLayout *layout = &node->layout;-- gDepth++;-- const bool needToVisitNode =- (node->isDirty && layout->generationCount != gCurrentGenerationCount) ||- layout->lastParentDirection != parentDirection;-- if (needToVisitNode) {- // Invalidate the cached results.- layout->nextCachedMeasurementsIndex = 0;- layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1;- layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1;- layout->cachedLayout.computedWidth = -1;- layout->cachedLayout.computedHeight = -1;- }-- YGCachedMeasurement *cachedResults = NULL;-- // 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 (node->measure) {- const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth);- const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth);-- // First, try to use the layout cache.- if (YGNodeCanUseCachedMeasurement(widthMeasureMode,- availableWidth,- heightMeasureMode,- availableHeight,- layout->cachedLayout.widthMeasureMode,- layout->cachedLayout.availableWidth,- layout->cachedLayout.heightMeasureMode,- layout->cachedLayout.availableHeight,- layout->cachedLayout.computedWidth,- layout->cachedLayout.computedHeight,- marginAxisRow,- marginAxisColumn)) {- cachedResults = &layout->cachedLayout;- } else {- // Try to use the measurement cache.- for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {- if (YGNodeCanUseCachedMeasurement(widthMeasureMode,- availableWidth,- heightMeasureMode,- availableHeight,- layout->cachedMeasurements[i].widthMeasureMode,- layout->cachedMeasurements[i].availableWidth,- layout->cachedMeasurements[i].heightMeasureMode,- layout->cachedMeasurements[i].availableHeight,- layout->cachedMeasurements[i].computedWidth,- layout->cachedMeasurements[i].computedHeight,- marginAxisRow,- marginAxisColumn)) {- cachedResults = &layout->cachedMeasurements[i];- break;- }- }- }- } else if (performLayout) {- if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) &&- YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) &&- layout->cachedLayout.widthMeasureMode == widthMeasureMode &&- layout->cachedLayout.heightMeasureMode == heightMeasureMode) {- cachedResults = &layout->cachedLayout;- }- } else {- for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {- if (YGFloatsEqual(layout->cachedMeasurements[i].availableWidth, availableWidth) &&- YGFloatsEqual(layout->cachedMeasurements[i].availableHeight, availableHeight) &&- layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&- layout->cachedMeasurements[i].heightMeasureMode == heightMeasureMode) {- cachedResults = &layout->cachedMeasurements[i];- break;- }- }- }-- if (!needToVisitNode && cachedResults != NULL) {- layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth;- layout->measuredDimensions[YGDimensionHeight] = cachedResults->computedHeight;-- if (gPrintChanges && gPrintSkips) {- printf("%s%d.{[skipped] ", YGSpacer(gDepth), gDepth);- if (node->print) {- node->print(node);- }- printf("wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",- YGMeasureModeName(widthMeasureMode, performLayout),- YGMeasureModeName(heightMeasureMode, performLayout),- availableWidth,- availableHeight,- cachedResults->computedWidth,- cachedResults->computedHeight,- reason);- }- } else {- if (gPrintChanges) {- printf("%s%d.{%s", YGSpacer(gDepth), gDepth, needToVisitNode ? "*" : "");- if (node->print) {- node->print(node);- }- printf("wm: %s, hm: %s, aw: %f ah: %f %s\n",- YGMeasureModeName(widthMeasureMode, performLayout),- YGMeasureModeName(heightMeasureMode, performLayout),- availableWidth,- availableHeight,- reason);- }-- YGNodelayoutImpl(node,- availableWidth,- availableHeight,- parentDirection,- widthMeasureMode,- heightMeasureMode,- parentWidth,- parentHeight,- performLayout,- config);-- if (gPrintChanges) {- printf("%s%d.}%s", YGSpacer(gDepth), gDepth, needToVisitNode ? "*" : "");- if (node->print) {- node->print(node);- }- printf("wm: %s, hm: %s, d: (%f, %f) %s\n",- YGMeasureModeName(widthMeasureMode, performLayout),- YGMeasureModeName(heightMeasureMode, performLayout),- layout->measuredDimensions[YGDimensionWidth],- layout->measuredDimensions[YGDimensionHeight],- reason);- }-- layout->lastParentDirection = parentDirection;-- if (cachedResults == NULL) {- if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) {- if (gPrintChanges) {- printf("Out of cache entries!\n");- }- layout->nextCachedMeasurementsIndex = 0;- }-- YGCachedMeasurement *newCacheEntry;- if (performLayout) {- // Use the single layout cache entry.- newCacheEntry = &layout->cachedLayout;- } else {- // Allocate a new measurement cache entry.- newCacheEntry = &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];- layout->nextCachedMeasurementsIndex++;- }-- newCacheEntry->availableWidth = availableWidth;- newCacheEntry->availableHeight = availableHeight;- newCacheEntry->widthMeasureMode = widthMeasureMode;- newCacheEntry->heightMeasureMode = heightMeasureMode;- newCacheEntry->computedWidth = layout->measuredDimensions[YGDimensionWidth];- newCacheEntry->computedHeight = layout->measuredDimensions[YGDimensionHeight];- }- }-- if (performLayout) {- node->layout.dimensions[YGDimensionWidth] = node->layout.measuredDimensions[YGDimensionWidth];- node->layout.dimensions[YGDimensionHeight] = node->layout.measuredDimensions[YGDimensionHeight];- node->hasNewLayout = true;- node->isDirty = false;- }-- gDepth--;- layout->generationCount = gCurrentGenerationCount;- return (needToVisitNode || cachedResults == NULL);-}--void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint) {- YG_ASSERT(pixelsInPoint >= 0.0f, "Scale factor should not be less than zero");- // We store points for Pixel as we will use it for rounding- if (pixelsInPoint == 0.0f) {- // Zero is used to skip rounding- config->pointScaleFactor = 0.0f;- } else {- config->pointScaleFactor = 1.0f / pixelsInPoint;- }-}--static void YGRoundToPixelGrid(const YGNodeRef node, const float pointScaleFactor) {- if (pointScaleFactor == 0.0f) {- return;- }- const float nodeLeft = node->layout.position[YGEdgeLeft];- const float nodeTop = node->layout.position[YGEdgeTop];-- // To round correctly to the pixel grid, first we calculate left and top coordinates- float fractialLeft = fmodf(nodeLeft, pointScaleFactor);- float fractialTop = fmodf(nodeTop, pointScaleFactor);- float roundedLeft = nodeLeft - fractialLeft;- float roundedTop = nodeTop - fractialTop;-- // To do the actual rounding we check if leftover fraction is bigger or equal than half of the grid step- if (fractialLeft >= pointScaleFactor / 2.0f) {- roundedLeft += pointScaleFactor;- fractialLeft -= pointScaleFactor;- }- if (fractialTop >= pointScaleFactor / 2.0f) {- roundedTop += pointScaleFactor;- fractialTop -= pointScaleFactor;- }- node->layout.position[YGEdgeLeft] = roundedLeft;- node->layout.position[YGEdgeTop] = roundedTop;-- // Now we round width and height in the same way accounting for fractial leftovers from rounding position- const float adjustedWidth = fractialLeft + node->layout.dimensions[YGDimensionWidth];- const float adjustedHeight = fractialTop + node->layout.dimensions[YGDimensionHeight];- float roundedWidth = adjustedWidth - fmodf(adjustedWidth, pointScaleFactor);- float roundedHeight = adjustedHeight - fmodf(adjustedHeight, pointScaleFactor);-- if (adjustedWidth - roundedWidth >= pointScaleFactor / 2.0f) {- roundedWidth += pointScaleFactor;- }- if (adjustedHeight - roundedHeight >= pointScaleFactor / 2.0f) {- roundedHeight += pointScaleFactor;- }- node->layout.dimensions[YGDimensionWidth] = roundedWidth;- node->layout.dimensions[YGDimensionHeight] = roundedHeight;-- const uint32_t childCount = YGNodeListCount(node->children);- for (uint32_t i = 0; i < childCount; i++) {- YGRoundToPixelGrid(YGNodeGetChild(node, i), pointScaleFactor);- }-}--void YGNodeCalculateLayout(const YGNodeRef node,- const float parentWidth,- const float parentHeight,- const YGDirection parentDirection) {- // 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++;-- YGResolveDimensions(node);-- float width = YGUndefined;- YGMeasureMode widthMeasureMode = YGMeasureModeUndefined;- if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, parentWidth)) {- width = YGResolveValue(node->resolvedDimensions[dim[YGFlexDirectionRow]], parentWidth) +- YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth);- widthMeasureMode = YGMeasureModeExactly;- } else if (YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth) >= 0.0f) {- width = YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth);- widthMeasureMode = YGMeasureModeAtMost;- } else {- width = parentWidth;- widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined : YGMeasureModeExactly;- }-- float height = YGUndefined;- YGMeasureMode heightMeasureMode = YGMeasureModeUndefined;- if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, parentHeight)) {- height = YGResolveValue(node->resolvedDimensions[dim[YGFlexDirectionColumn]], parentHeight) +- YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth);- heightMeasureMode = YGMeasureModeExactly;- } else if (YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight) >=- 0.0f) {- height = YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight);- heightMeasureMode = YGMeasureModeAtMost;- } else {- height = parentHeight;- heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined : YGMeasureModeExactly;- }-- if (YGLayoutNodeInternal(node,- width,- height,- parentDirection,- widthMeasureMode,- heightMeasureMode,- parentWidth,- parentHeight,- true,- "initial",- node->config)) {- YGNodeSetPosition(node, node->layout.direction, parentWidth, parentHeight, parentWidth);-- if (YGConfigIsExperimentalFeatureEnabled(node->config, YGExperimentalFeatureRounding)) {- YGRoundToPixelGrid(node, node->config->pointScaleFactor);- }-- if (gPrintTree) {- YGNodePrint(node, YGPrintOptionsLayout | YGPrintOptionsChildren | YGPrintOptionsStyle);- }- }-}--void YGSetLogger(YGLogger logger) {- gLogger = logger;-}--void YGLog(YGLogLevel level, const char *format, ...) {- va_list args;- va_start(args, format);- gLogger(level, format, args);- va_end(args);-}--void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config,- const YGExperimentalFeature feature,- const bool enabled) {- config->experimentalFeatures[feature] = enabled;-}--inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config,- const YGExperimentalFeature feature) {- return config->experimentalFeatures[feature];-}--void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) {- config->useWebDefaults = enabled;-}--bool YGConfigGetUseWebDefaults(const YGConfigRef config) {- return config->useWebDefaults;-}--void YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree) {- YG_ASSERT(gNodeInstanceCount == 0, "Cannot set memory functions: all node must be freed first");- YG_ASSERT((ygmalloc == NULL && yccalloc == NULL && ygrealloc == NULL && ygfree == NULL) ||- (ygmalloc != NULL && yccalloc != NULL && ygrealloc != NULL && ygfree != NULL),- "Cannot set memory functions: functions must be all NULL or Non-NULL");-- if (ygmalloc == NULL || yccalloc == NULL || ygrealloc == NULL || ygfree == NULL) {- gYGMalloc = &malloc;- gYGCalloc = &calloc;- gYGRealloc = &realloc;- gYGFree = &free;- } else {- gYGMalloc = ygmalloc;- gYGCalloc = yccalloc;- gYGRealloc = ygrealloc;- gYGFree = ygfree;- }-}
+ yoga/Yoga.cpp view
@@ -0,0 +1,3533 @@+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "Yoga.h" +#include <string.h> +#include <algorithm> +#include "YGNodePrint.h" +#include "Yoga-internal.h" + +#ifdef _MSC_VER +#include <float.h> +#ifndef isnan +#define isnan _isnan +#endif +#ifndef __cplusplus +#define inline __inline +#endif + +/* 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 + +#ifdef ANDROID +static int YGAndroidLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args); +#else +static int YGDefaultLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args); +#endif + +static YGConfig gYGConfigDefaults = { + .experimentalFeatures = + { + [YGExperimentalFeatureWebFlexBasis] = false, + }, + .useWebDefaults = false, + .useLegacyStretchBehaviour = false, + .pointScaleFactor = 1.0f, +#ifdef ANDROID + .logger = &YGAndroidLog, +#else + .logger = &YGDefaultLog, +#endif + .cloneNodeCallback = nullptr, + .context = nullptr, +}; + +static void YGNodeMarkDirtyInternal(const YGNodeRef node); + +static YGValue YGValueZero = {.value = 0, .unit = YGUnitPoint}; + +static bool YGNodeListDelete(YGVector& list, const YGNodeRef node) { + std::vector<YGNodeRef>::iterator p = + std::find(list.begin(), list.end(), node); + if (p != list.end()) { + list.erase(p); + return true; + } + return false; +} + +#ifdef ANDROID +#include <android/log.h> +static int YGAndroidLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + int androidLevel = YGLogLevelDebug; + switch (level) { + case YGLogLevelFatal: + androidLevel = ANDROID_LOG_FATAL; + break; + case YGLogLevelError: + androidLevel = ANDROID_LOG_ERROR; + break; + case YGLogLevelWarn: + androidLevel = ANDROID_LOG_WARN; + break; + case YGLogLevelInfo: + androidLevel = ANDROID_LOG_INFO; + break; + case YGLogLevelDebug: + androidLevel = ANDROID_LOG_DEBUG; + break; + case YGLogLevelVerbose: + androidLevel = ANDROID_LOG_VERBOSE; + break; + } + const int result = __android_log_vprint(androidLevel, "yoga", format, args); + return result; +} +#else +#define YG_UNUSED(x) (void)(x); + +static int YGDefaultLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + YG_UNUSED(config); + YG_UNUSED(node); + switch (level) { + case YGLogLevelError: + case YGLogLevelFatal: + return vfprintf(stderr, format, args); + case YGLogLevelWarn: + case YGLogLevelInfo: + case YGLogLevelDebug: + case YGLogLevelVerbose: + default: + return vprintf(format, args); + } +} + +#undef YG_UNUSED +#endif + +bool YGFloatIsUndefined(const float value) { + return isnan(value); +} + +const YGValue* YGComputedEdgeValue( + const YGValue edges[YGEdgeCount], + const YGEdge edge, + const YGValue* const defaultValue) { + if (edges[edge].unit != YGUnitUndefined) { + return &edges[edge]; + } + + if ((edge == YGEdgeTop || edge == YGEdgeBottom) && + edges[YGEdgeVertical].unit != YGUnitUndefined) { + return &edges[YGEdgeVertical]; + } + + if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || edge == YGEdgeEnd) && + edges[YGEdgeHorizontal].unit != YGUnitUndefined) { + return &edges[YGEdgeHorizontal]; + } + + if (edges[YGEdgeAll].unit != YGUnitUndefined) { + return &edges[YGEdgeAll]; + } + + if (edge == YGEdgeStart || edge == YGEdgeEnd) { + return &YGValueUndefined; + } + + return defaultValue; +} + +static inline float YGResolveValue(const YGValue *const value, const float parentSize) { + switch (value->unit) { + case YGUnitUndefined: + case YGUnitAuto: + return YGUndefined; + case YGUnitPoint: + return value->value; + case YGUnitPercent: + return value->value * parentSize / 100.0f; + } + return YGUndefined; +} + +static inline float YGResolveValueMargin(const YGValue *const value, const float parentSize) { + return value->unit == YGUnitAuto ? 0 : YGResolveValue(value, parentSize); +} + +int32_t gNodeInstanceCount = 0; +int32_t gConfigInstanceCount = 0; + +WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) { + const YGNodeRef node = (const YGNodeRef)malloc(sizeof(YGNode)); + YGAssertWithConfig( + config, node != nullptr, "Could not allocate memory for node"); + gNodeInstanceCount++; + + memcpy(node, &gYGNodeDefaults, sizeof(YGNode)); + if (config->useWebDefaults) { + node->style.flexDirection = YGFlexDirectionRow; + node->style.alignContent = YGAlignStretch; + } + node->config = config; + return node; +} + +YGNodeRef YGNodeNew(void) { + return YGNodeNewWithConfig(&gYGConfigDefaults); +} + +YGNodeRef YGNodeClone(YGNodeRef oldNode) { + YGNodeRef node = new YGNode(*oldNode); + YGAssertWithConfig( + oldNode->config, node != nullptr, "Could not allocate memory for node"); + gNodeInstanceCount++; + node->parent = nullptr; + return node; +} + +void YGNodeFree(const YGNodeRef node) { + if (node->parent) { + YGNodeListDelete(node->parent->children, node); + node->parent = nullptr; + } + + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + child->parent = nullptr; + } + + YGVector().swap(node->children); + free(node); + gNodeInstanceCount--; +} + +void YGNodeFreeRecursive(const YGNodeRef root) { + while (YGNodeGetChildCount(root) > 0) { + const YGNodeRef child = YGNodeGetChild(root, 0); + if (child->parent != root) { + // Don't free shared nodes that we don't own. + break; + } + YGNodeRemoveChild(root, child); + YGNodeFreeRecursive(child); + } + YGNodeFree(root); +} + +void YGNodeReset(const YGNodeRef node) { + YGAssertWithNode(node, + YGNodeGetChildCount(node) == 0, + "Cannot reset a node which still has children attached"); + YGAssertWithNode( + node, + node->parent == nullptr, + "Cannot reset a node still attached to a parent"); + + YGVector().swap(node->children); + + const YGConfigRef config = node->config; + memcpy(node, &gYGNodeDefaults, sizeof(YGNode)); + if (config->useWebDefaults) { + node->style.flexDirection = YGFlexDirectionRow; + node->style.alignContent = YGAlignStretch; + } + node->config = config; +} + +int32_t YGNodeGetInstanceCount(void) { + return gNodeInstanceCount; +} + +int32_t YGConfigGetInstanceCount(void) { + return gConfigInstanceCount; +} + +// Export only for C# +YGConfigRef YGConfigGetDefault() { + return &gYGConfigDefaults; +} + +YGConfigRef YGConfigNew(void) { + const YGConfigRef config = (const YGConfigRef)malloc(sizeof(YGConfig)); + YGAssert(config != nullptr, "Could not allocate memory for config"); + + gConfigInstanceCount++; + memcpy(config, &gYGConfigDefaults, sizeof(YGConfig)); + return config; +} + +void YGConfigFree(const YGConfigRef config) { + free(config); + gConfigInstanceCount--; +} + +void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) { + memcpy(dest, src, sizeof(YGConfig)); +} + +static void YGNodeMarkDirtyInternal(const YGNodeRef node) { + if (!node->isDirty) { + node->isDirty = true; + node->layout.computedFlexBasis = YGUndefined; + if (node->parent) { + YGNodeMarkDirtyInternal(node->parent); + } + } +} + +void YGNodeSetMeasureFunc(const YGNodeRef node, YGMeasureFunc measureFunc) { + if (measureFunc == nullptr) { + node->measure = nullptr; + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate places in Litho + node->nodeType = YGNodeTypeDefault; + } else { + YGAssertWithNode( + node, + YGNodeGetChildCount(node) == 0, + "Cannot set measure function: Nodes with measure functions cannot have children."); + node->measure = measureFunc; + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate places in Litho + node->nodeType = YGNodeTypeText; + } +} + +YGMeasureFunc YGNodeGetMeasureFunc(const YGNodeRef node) { + return node->measure; +} + +void YGNodeSetBaselineFunc(const YGNodeRef node, YGBaselineFunc baselineFunc) { + node->baseline = baselineFunc; +} + +YGBaselineFunc YGNodeGetBaselineFunc(const YGNodeRef node) { + return node->baseline; +} + +static void YGCloneChildrenIfNeeded(const YGNodeRef parent) { + // YGNodeRemoveChild has a forked variant of this algorithm optimized for deletions. + const uint32_t childCount = YGNodeGetChildCount(parent); + if (childCount == 0) { + // This is an empty set. Nothing to clone. + return; + } + + const YGNodeRef firstChild = YGNodeGetChild(parent, 0); + if (firstChild->parent == parent) { + // If the first child has this node as its parent, we assume that it is already unique. + // We can do this because if we have it has a child, that means that its parent was at some + // point cloned which made that subtree immutable. + // We also assume that all its sibling are cloned as well. + return; + } + + const YGNodeClonedFunc cloneNodeCallback = parent->config->cloneNodeCallback; + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = parent->children[i]; + const YGNodeRef newChild = YGNodeClone(oldChild); + parent->children[i] = newChild; + newChild->parent = parent; + if (cloneNodeCallback) { + cloneNodeCallback(oldChild, newChild, parent, i); + } + } +} + +void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) { + YGAssertWithNode( + node, + child->parent == nullptr, + "Child already has a parent, it must be removed first."); + YGAssertWithNode( + node, + node->measure == nullptr, + "Cannot add child: Nodes with measure functions cannot have children."); + + YGCloneChildrenIfNeeded(node); + node->children.insert(node->children.begin() + index, child); + child->parent = node; + YGNodeMarkDirtyInternal(node); +} + +void YGNodeRemoveChild(const YGNodeRef parent, const YGNodeRef excludedChild) { + // This algorithm is a forked variant from YGCloneChildrenIfNeeded that excludes a child. + const uint32_t childCount = YGNodeGetChildCount(parent); + + if (childCount == 0) { + // This is an empty set. Nothing to remove. + return; + } + const YGNodeRef firstChild = YGNodeGetChild(parent, 0); + if (firstChild->parent == parent) { + // If the first child has this node as its parent, we assume that it is already unique. + // We can now try to delete a child in this list. + if (YGNodeListDelete(parent->children, excludedChild)) { + excludedChild->layout = gYGNodeDefaults.layout; // layout is no longer valid + excludedChild->parent = nullptr; + YGNodeMarkDirtyInternal(parent); + } + return; + } + // Otherwise we have to clone the node list except for the child we're trying to delete. + // We don't want to simply clone all children, because then the host will need to free + // the clone of the child that was just deleted. + const YGNodeClonedFunc cloneNodeCallback = parent->config->cloneNodeCallback; + uint32_t nextInsertIndex = 0; + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = parent->children[i]; + if (excludedChild == oldChild) { + // Ignore the deleted child. Don't reset its layout or parent since it is still valid + // in the other parent. However, since this parent has now changed, we need to mark it + // as dirty. + YGNodeMarkDirtyInternal(parent); + continue; + } + const YGNodeRef newChild = YGNodeClone(oldChild); + parent->children[nextInsertIndex] = newChild; + newChild->parent = parent; + if (cloneNodeCallback) { + cloneNodeCallback(oldChild, newChild, parent, nextInsertIndex); + } + nextInsertIndex++; + } + while (nextInsertIndex < childCount) { + parent->children.erase(parent->children.begin() + nextInsertIndex); + nextInsertIndex++; + } +} + +void YGNodeRemoveAllChildren(const YGNodeRef parent) { + const uint32_t childCount = YGNodeGetChildCount(parent); + if (childCount == 0) { + // This is an empty set already. Nothing to do. + return; + } + const YGNodeRef firstChild = YGNodeGetChild(parent, 0); + if (firstChild->parent == parent) { + // If the first child has this node as its parent, we assume that this child set is unique. + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = YGNodeGetChild(parent, i); + oldChild->layout = gYGNodeDefaults.layout; // layout is no longer valid + oldChild->parent = nullptr; + } + YGVector().swap(parent->children); + YGNodeMarkDirtyInternal(parent); + return; + } + // Otherwise, we are not the owner of the child set. We don't have to do anything to clear it. + parent->children = YGVector(); + YGNodeMarkDirtyInternal(parent); +} + +YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) { + if (index < node->children.size()) { + return node->children[index]; + } + return nullptr; +} + +YGNodeRef YGNodeGetParent(const YGNodeRef node) { + return node->parent; +} + +uint32_t YGNodeGetChildCount(const YGNodeRef node) { + return node->children.size(); +} + +void YGNodeMarkDirty(const YGNodeRef node) { + YGAssertWithNode( + node, + node->measure != nullptr, + "Only leaf nodes with custom measure functions" + "should manually mark themselves as dirty"); + + YGNodeMarkDirtyInternal(node); +} + +bool YGNodeIsDirty(const YGNodeRef node) { + return node->isDirty; +} + +void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) { + if (memcmp(&dstNode->style, &srcNode->style, sizeof(YGStyle)) != 0) { + memcpy(&dstNode->style, &srcNode->style, sizeof(YGStyle)); + YGNodeMarkDirtyInternal(dstNode); + } +} + +static inline float YGResolveFlexGrow(const YGNodeRef node) { + // Root nodes flexGrow should always be 0 + if (node->parent == nullptr) { + return 0.0; + } + if (!YGFloatIsUndefined(node->style.flexGrow)) { + return node->style.flexGrow; + } + if (!YGFloatIsUndefined(node->style.flex) && node->style.flex > 0.0f) { + return node->style.flex; + } + return kDefaultFlexGrow; +} + +float YGNodeStyleGetFlexGrow(const YGNodeRef node) { + return YGFloatIsUndefined(node->style.flexGrow) ? kDefaultFlexGrow : node->style.flexGrow; +} + +float YGNodeStyleGetFlexShrink(const YGNodeRef node) { + return YGFloatIsUndefined(node->style.flexShrink) + ? (node->config->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink) + : node->style.flexShrink; +} + +static inline float YGNodeResolveFlexShrink(const YGNodeRef node) { + // Root nodes flexShrink should always be 0 + if (node->parent == nullptr) { + return 0.0; + } + if (!YGFloatIsUndefined(node->style.flexShrink)) { + return node->style.flexShrink; + } + if (!node->config->useWebDefaults && !YGFloatIsUndefined(node->style.flex) && + node->style.flex < 0.0f) { + return -node->style.flex; + } + return node->config->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink; +} + +static inline const YGValue *YGNodeResolveFlexBasisPtr(const YGNodeRef node) { + if (node->style.flexBasis.unit != YGUnitAuto && node->style.flexBasis.unit != YGUnitUndefined) { + return &node->style.flexBasis; + } + if (!YGFloatIsUndefined(node->style.flex) && node->style.flex > 0.0f) { + return node->config->useWebDefaults ? &YGValueAuto : &YGValueZero; + } + return &YGValueAuto; +} + +#define YG_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \ + void YGNodeSet##name(const YGNodeRef node, type paramName) { \ + node->instanceName = paramName; \ + } \ + \ + type YGNodeGet##name(const YGNodeRef node) { \ + return node->instanceName; \ + } + +#define YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + if (node->style.instanceName != paramName) { \ + node->style.instanceName = paramName; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } + +#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->style.instanceName.value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->style.instanceName.unit != value.unit) { \ + node->style.instanceName = value; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const type paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = \ + YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \ + }; \ + if ((node->style.instanceName.value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->style.instanceName.unit != value.unit) { \ + node->style.instanceName = value; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } + +#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->style.instanceName.value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->style.instanceName.unit != value.unit) { \ + node->style.instanceName = value; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const type paramName) { \ + if (node->style.instanceName.value != paramName || \ + node->style.instanceName.unit != YGUnitPercent) { \ + node->style.instanceName.value = paramName; \ + node->style.instanceName.unit = \ + YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \ + if (node->style.instanceName.unit != YGUnitAuto) { \ + node->style.instanceName.value = YGUndefined; \ + node->style.instanceName.unit = YGUnitAuto; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } + +#define YG_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \ + YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \ + \ + type YGNodeStyleGet##name(const YGNodeRef node) { \ + return node->style.instanceName; \ + } + +#define YG_NODE_STYLE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \ + YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(float, name, paramName, instanceName) \ + \ + type YGNodeStyleGet##name(const YGNodeRef node) { \ + return node->style.instanceName; \ + } + +#define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(type, name, paramName, instanceName) \ + YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(float, name, paramName, instanceName) \ + \ + type YGNodeStyleGet##name(const YGNodeRef node) { \ + return node->style.instanceName; \ + } + +#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \ + void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \ + if (node->style.instanceName[edge].unit != YGUnitAuto) { \ + node->style.instanceName[edge].value = YGUndefined; \ + node->style.instanceName[edge].unit = YGUnitAuto; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } + +#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->style.instanceName[edge].value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->style.instanceName[edge].unit != value.unit) { \ + node->style.instanceName[edge] = value; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = \ + YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \ + }; \ + if ((node->style.instanceName[edge].value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->style.instanceName[edge].unit != value.unit) { \ + node->style.instanceName[edge] = value; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } \ + \ + WIN_STRUCT(type) \ + YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ + return WIN_STRUCT_REF(node->style.instanceName[edge]); \ + } + +#define YG_NODE_STYLE_EDGE_PROPERTY_IMPL(type, name, paramName, instanceName) \ + void YGNodeStyleSet##name( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->style.instanceName[edge].value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->style.instanceName[edge].unit != value.unit) { \ + node->style.instanceName[edge] = value; \ + YGNodeMarkDirtyInternal(node); \ + } \ + } \ + \ + float YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ + return node->style.instanceName[edge].value; \ + } + +#define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \ + type YGNodeLayoutGet##name(const YGNodeRef node) { \ + return node->layout.instanceName; \ + } + +#define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \ + type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \ + YGAssertWithNode( \ + node, \ + edge <= YGEdgeEnd, \ + "Cannot get layout properties of multi-edge shorthands"); \ + \ + if (edge == YGEdgeLeft) { \ + if (node->layout.direction == YGDirectionRTL) { \ + return node->layout.instanceName[YGEdgeEnd]; \ + } else { \ + return node->layout.instanceName[YGEdgeStart]; \ + } \ + } \ + \ + if (edge == YGEdgeRight) { \ + if (node->layout.direction == YGDirectionRTL) { \ + return node->layout.instanceName[YGEdgeStart]; \ + } else { \ + return node->layout.instanceName[YGEdgeEnd]; \ + } \ + } \ + \ + return node->layout.instanceName[edge]; \ + } + +YG_NODE_PROPERTY_IMPL(void *, Context, context, context); +YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print); +YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout); +YG_NODE_PROPERTY_IMPL(YGNodeType, NodeType, nodeType, nodeType); + +YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction); +YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection); +YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf); +YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType); +YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap); +YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow); +YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display); + +YG_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex); +YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexGrow, flexGrow, flexGrow); +YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexShrink, flexShrink, flexShrink); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, FlexBasis, flexBasis, flexBasis); + +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding); +YG_NODE_STYLE_EDGE_PROPERTY_IMPL(float, Border, border, border); + +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]); + +// Yoga specific properties, not compatible with flexbox specification +YG_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio); + +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]); +YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction); +YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow); + +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin); +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border); +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding); + +uint32_t gCurrentGenerationCount = 0; + +bool YGLayoutNodeInternal(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight, + const bool performLayout, + const char *reason, + const YGConfigRef config); + +bool YGValueEqual(const YGValue a, const YGValue b) { + if (a.unit != b.unit) { + return false; + } + + if (a.unit == YGUnitUndefined) { + return true; + } + + return fabs(a.value - b.value) < 0.0001f; +} + +static inline void YGResolveDimensions(YGNodeRef node) { + for (uint32_t dim = YGDimensionWidth; dim < YGDimensionCount; dim++) { + if (node->style.maxDimensions[dim].unit != YGUnitUndefined && + YGValueEqual(node->style.maxDimensions[dim], node->style.minDimensions[dim])) { + node->resolvedDimensions[dim] = &node->style.maxDimensions[dim]; + } else { + node->resolvedDimensions[dim] = &node->style.dimensions[dim]; + } + } +} + +bool YGFloatsEqual(const float a, const float b) { + if (YGFloatIsUndefined(a)) { + return YGFloatIsUndefined(b); + } + return fabs(a - b) < 0.0001f; +} + +static void YGNodePrintInternal(const YGNodeRef node, + const YGPrintOptions options) { + std::string str; + facebook::yoga::YGNodeToString(&str, node, options, 0); + YGLog(node, YGLogLevelDebug, str.c_str()); +} + +void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) { + YGNodePrintInternal(node, options); +} + +static const YGEdge leading[4] = { + [YGFlexDirectionColumn] = YGEdgeTop, + [YGFlexDirectionColumnReverse] = YGEdgeBottom, + [YGFlexDirectionRow] = YGEdgeLeft, + [YGFlexDirectionRowReverse] = YGEdgeRight, +}; +static const YGEdge trailing[4] = { + [YGFlexDirectionColumn] = YGEdgeBottom, + [YGFlexDirectionColumnReverse] = YGEdgeTop, + [YGFlexDirectionRow] = YGEdgeRight, + [YGFlexDirectionRowReverse] = YGEdgeLeft, +}; +static const YGEdge pos[4] = { + [YGFlexDirectionColumn] = YGEdgeTop, + [YGFlexDirectionColumnReverse] = YGEdgeBottom, + [YGFlexDirectionRow] = YGEdgeLeft, + [YGFlexDirectionRowReverse] = YGEdgeRight, +}; +static const YGDimension dim[4] = { + [YGFlexDirectionColumn] = YGDimensionHeight, + [YGFlexDirectionColumnReverse] = YGDimensionHeight, + [YGFlexDirectionRow] = YGDimensionWidth, + [YGFlexDirectionRowReverse] = YGDimensionWidth, +}; + +static inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionRow || flexDirection == YGFlexDirectionRowReverse; +} + +static inline bool YGFlexDirectionIsColumn(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionColumn || flexDirection == YGFlexDirectionColumnReverse; +} + +static inline float YGNodeLeadingMargin(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeStart].unit != YGUnitUndefined) { + return YGResolveValueMargin(&node->style.margin[YGEdgeStart], widthSize); + } + + return YGResolveValueMargin(YGComputedEdgeValue(node->style.margin, leading[axis], &YGValueZero), + widthSize); +} + +static float YGNodeTrailingMargin(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeEnd].unit != YGUnitUndefined) { + return YGResolveValueMargin(&node->style.margin[YGEdgeEnd], widthSize); + } + + return YGResolveValueMargin(YGComputedEdgeValue(node->style.margin, trailing[axis], &YGValueZero), + widthSize); +} + +static float YGNodeLeadingPadding(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && node->style.padding[YGEdgeStart].unit != YGUnitUndefined && + YGResolveValue(&node->style.padding[YGEdgeStart], widthSize) >= 0.0f) { + return YGResolveValue(&node->style.padding[YGEdgeStart], widthSize); + } + + return fmaxf(YGResolveValue(YGComputedEdgeValue(node->style.padding, leading[axis], &YGValueZero), + widthSize), + 0.0f); +} + +static float YGNodeTrailingPadding(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && node->style.padding[YGEdgeEnd].unit != YGUnitUndefined && + YGResolveValue(&node->style.padding[YGEdgeEnd], widthSize) >= 0.0f) { + return YGResolveValue(&node->style.padding[YGEdgeEnd], widthSize); + } + + return fmaxf(YGResolveValue(YGComputedEdgeValue(node->style.padding, trailing[axis], &YGValueZero), + widthSize), + 0.0f); +} + +static float YGNodeLeadingBorder(const YGNodeRef node, const YGFlexDirection axis) { + if (YGFlexDirectionIsRow(axis) && node->style.border[YGEdgeStart].unit != YGUnitUndefined && + node->style.border[YGEdgeStart].value >= 0.0f) { + return node->style.border[YGEdgeStart].value; + } + + return fmaxf(YGComputedEdgeValue(node->style.border, leading[axis], &YGValueZero)->value, 0.0f); +} + +static float YGNodeTrailingBorder(const YGNodeRef node, const YGFlexDirection axis) { + if (YGFlexDirectionIsRow(axis) && node->style.border[YGEdgeEnd].unit != YGUnitUndefined && + node->style.border[YGEdgeEnd].value >= 0.0f) { + return node->style.border[YGEdgeEnd].value; + } + + return fmaxf(YGComputedEdgeValue(node->style.border, trailing[axis], &YGValueZero)->value, 0.0f); +} + +static inline float YGNodeLeadingPaddingAndBorder(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return YGNodeLeadingPadding(node, axis, widthSize) + YGNodeLeadingBorder(node, axis); +} + +static inline float YGNodeTrailingPaddingAndBorder(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return YGNodeTrailingPadding(node, axis, widthSize) + YGNodeTrailingBorder(node, axis); +} + +static inline float YGNodeMarginForAxis(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return YGNodeLeadingMargin(node, axis, widthSize) + YGNodeTrailingMargin(node, axis, widthSize); +} + +static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return YGNodeLeadingPaddingAndBorder(node, axis, widthSize) + + YGNodeTrailingPaddingAndBorder(node, axis, widthSize); +} + +static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) { + const YGAlign align = + child->style.alignSelf == YGAlignAuto ? node->style.alignItems : child->style.alignSelf; + if (align == YGAlignBaseline && YGFlexDirectionIsColumn(node->style.flexDirection)) { + return YGAlignFlexStart; + } + return align; +} + +static inline YGDirection YGNodeResolveDirection(const YGNodeRef node, + const YGDirection parentDirection) { + if (node->style.direction == YGDirectionInherit) { + return parentDirection > YGDirectionInherit ? parentDirection : YGDirectionLTR; + } else { + return node->style.direction; + } +} + +static float YGBaseline(const YGNodeRef node) { + if (node->baseline != nullptr) { + const float baseline = node->baseline(node, + node->layout.measuredDimensions[YGDimensionWidth], + node->layout.measuredDimensions[YGDimensionHeight]); + YGAssertWithNode(node, + !YGFloatIsUndefined(baseline), + "Expect custom baseline function to not return NaN"); + return baseline; + } + + YGNodeRef baselineChild = nullptr; + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->lineIndex > 0) { + break; + } + if (child->style.positionType == YGPositionTypeAbsolute) { + continue; + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + baselineChild = child; + break; + } + + if (baselineChild == nullptr) { + baselineChild = child; + } + } + + if (baselineChild == nullptr) { + return node->layout.measuredDimensions[YGDimensionHeight]; + } + + const float baseline = YGBaseline(baselineChild); + return baseline + baselineChild->layout.position[YGEdgeTop]; +} + +static inline YGFlexDirection YGResolveFlexDirection(const YGFlexDirection flexDirection, + const YGDirection direction) { + if (direction == YGDirectionRTL) { + if (flexDirection == YGFlexDirectionRow) { + return YGFlexDirectionRowReverse; + } else if (flexDirection == YGFlexDirectionRowReverse) { + return YGFlexDirectionRow; + } + } + + return flexDirection; +} + +static YGFlexDirection YGFlexDirectionCross(const YGFlexDirection flexDirection, + const YGDirection direction) { + return YGFlexDirectionIsColumn(flexDirection) + ? YGResolveFlexDirection(YGFlexDirectionRow, direction) + : YGFlexDirectionColumn; +} + +static inline bool YGNodeIsFlex(const YGNodeRef node) { + return (node->style.positionType == YGPositionTypeRelative && + (YGResolveFlexGrow(node) != 0 || YGNodeResolveFlexShrink(node) != 0)); +} + +static bool YGIsBaselineLayout(const YGNodeRef node) { + if (YGFlexDirectionIsColumn(node->style.flexDirection)) { + return false; + } + if (node->style.alignItems == YGAlignBaseline) { + return true; + } + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->style.positionType == YGPositionTypeRelative && + child->style.alignSelf == YGAlignBaseline) { + return true; + } + } + + return false; +} + +static inline float YGNodeDimWithMargin(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return node->layout.measuredDimensions[dim[axis]] + YGNodeLeadingMargin(node, axis, widthSize) + + YGNodeTrailingMargin(node, axis, widthSize); +} + +static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node, + const YGFlexDirection axis, + const float parentSize) { + return !(node->resolvedDimensions[dim[axis]]->unit == YGUnitAuto || + node->resolvedDimensions[dim[axis]]->unit == YGUnitUndefined || + (node->resolvedDimensions[dim[axis]]->unit == YGUnitPoint && + node->resolvedDimensions[dim[axis]]->value < 0.0f) || + (node->resolvedDimensions[dim[axis]]->unit == YGUnitPercent && + (node->resolvedDimensions[dim[axis]]->value < 0.0f || YGFloatIsUndefined(parentSize)))); +} + +static inline bool YGNodeIsLayoutDimDefined(const YGNodeRef node, const YGFlexDirection axis) { + const float value = node->layout.measuredDimensions[dim[axis]]; + return !YGFloatIsUndefined(value) && value >= 0.0f; +} + +static inline bool YGNodeIsLeadingPosDefined(const YGNodeRef node, const YGFlexDirection axis) { + return (YGFlexDirectionIsRow(axis) && + YGComputedEdgeValue(node->style.position, YGEdgeStart, &YGValueUndefined)->unit != + YGUnitUndefined) || + YGComputedEdgeValue(node->style.position, leading[axis], &YGValueUndefined)->unit != + YGUnitUndefined; +} + +static inline bool YGNodeIsTrailingPosDefined(const YGNodeRef node, const YGFlexDirection axis) { + return (YGFlexDirectionIsRow(axis) && + YGComputedEdgeValue(node->style.position, YGEdgeEnd, &YGValueUndefined)->unit != + YGUnitUndefined) || + YGComputedEdgeValue(node->style.position, trailing[axis], &YGValueUndefined)->unit != + YGUnitUndefined; +} + +static float YGNodeLeadingPosition(const YGNodeRef node, + const YGFlexDirection axis, + const float axisSize) { + if (YGFlexDirectionIsRow(axis)) { + const YGValue *leadingPosition = + YGComputedEdgeValue(node->style.position, YGEdgeStart, &YGValueUndefined); + if (leadingPosition->unit != YGUnitUndefined) { + return YGResolveValue(leadingPosition, axisSize); + } + } + + const YGValue *leadingPosition = + YGComputedEdgeValue(node->style.position, leading[axis], &YGValueUndefined); + + return leadingPosition->unit == YGUnitUndefined ? 0.0f + : YGResolveValue(leadingPosition, axisSize); +} + +static float YGNodeTrailingPosition(const YGNodeRef node, + const YGFlexDirection axis, + const float axisSize) { + if (YGFlexDirectionIsRow(axis)) { + const YGValue *trailingPosition = + YGComputedEdgeValue(node->style.position, YGEdgeEnd, &YGValueUndefined); + if (trailingPosition->unit != YGUnitUndefined) { + return YGResolveValue(trailingPosition, axisSize); + } + } + + const YGValue *trailingPosition = + YGComputedEdgeValue(node->style.position, trailing[axis], &YGValueUndefined); + + return trailingPosition->unit == YGUnitUndefined ? 0.0f + : YGResolveValue(trailingPosition, axisSize); +} + +static float YGNodeBoundAxisWithinMinAndMax(const YGNodeRef node, + const YGFlexDirection axis, + const float value, + const float axisSize) { + float min = YGUndefined; + float max = YGUndefined; + + if (YGFlexDirectionIsColumn(axis)) { + min = YGResolveValue(&node->style.minDimensions[YGDimensionHeight], axisSize); + max = YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], axisSize); + } else if (YGFlexDirectionIsRow(axis)) { + min = YGResolveValue(&node->style.minDimensions[YGDimensionWidth], axisSize); + max = YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], axisSize); + } + + float boundValue = value; + + if (!YGFloatIsUndefined(max) && max >= 0.0f && boundValue > max) { + boundValue = max; + } + + if (!YGFloatIsUndefined(min) && min >= 0.0f && boundValue < min) { + boundValue = min; + } + + return boundValue; +} + +static inline YGValue *YGMarginLeadingValue(const YGNodeRef node, const YGFlexDirection axis) { + if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeStart].unit != YGUnitUndefined) { + return &node->style.margin[YGEdgeStart]; + } else { + return &node->style.margin[leading[axis]]; + } +} + +static inline YGValue *YGMarginTrailingValue(const YGNodeRef node, const YGFlexDirection axis) { + if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeEnd].unit != YGUnitUndefined) { + return &node->style.margin[YGEdgeEnd]; + } else { + return &node->style.margin[trailing[axis]]; + } +} + +// Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't go +// below the +// padding and border amount. +static inline float YGNodeBoundAxis(const YGNodeRef node, + const YGFlexDirection axis, + const float value, + const float axisSize, + const float widthSize) { + return fmaxf(YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize), + YGNodePaddingAndBorderForAxis(node, axis, widthSize)); +} + +static void YGNodeSetChildTrailingPosition(const YGNodeRef node, + const YGNodeRef child, + const YGFlexDirection axis) { + const float size = child->layout.measuredDimensions[dim[axis]]; + child->layout.position[trailing[axis]] = + node->layout.measuredDimensions[dim[axis]] - size - child->layout.position[pos[axis]]; +} + +// If both left and right are defined, then use left. Otherwise return +// +left or -right depending on which is defined. +static float YGNodeRelativePosition(const YGNodeRef node, + const YGFlexDirection axis, + const float axisSize) { + return YGNodeIsLeadingPosDefined(node, axis) ? YGNodeLeadingPosition(node, axis, axisSize) + : -YGNodeTrailingPosition(node, axis, axisSize); +} + +static void YGConstrainMaxSizeForMode(const YGNodeRef node, + const enum YGFlexDirection axis, + const float parentAxisSize, + const float parentWidth, + YGMeasureMode *mode, + float *size) { + const float maxSize = YGResolveValue(&node->style.maxDimensions[dim[axis]], parentAxisSize) + + YGNodeMarginForAxis(node, axis, parentWidth); + switch (*mode) { + case YGMeasureModeExactly: + case YGMeasureModeAtMost: + *size = (YGFloatIsUndefined(maxSize) || *size < maxSize) ? *size : maxSize; + break; + case YGMeasureModeUndefined: + if (!YGFloatIsUndefined(maxSize)) { + *mode = YGMeasureModeAtMost; + *size = maxSize; + } + break; + } +} + +static void YGNodeSetPosition(const YGNodeRef node, + const YGDirection direction, + const float mainSize, + const float crossSize, + const float parentWidth) { + /* Root nodes should be always layouted as LTR, so we don't return negative values. */ + const YGDirection directionRespectingRoot = + node->parent != nullptr ? direction : YGDirectionLTR; + const YGFlexDirection mainAxis = + YGResolveFlexDirection(node->style.flexDirection, directionRespectingRoot); + const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, directionRespectingRoot); + + const float relativePositionMain = YGNodeRelativePosition(node, mainAxis, mainSize); + const float relativePositionCross = YGNodeRelativePosition(node, crossAxis, crossSize); + + node->layout.position[leading[mainAxis]] = + YGNodeLeadingMargin(node, mainAxis, parentWidth) + relativePositionMain; + node->layout.position[trailing[mainAxis]] = + YGNodeTrailingMargin(node, mainAxis, parentWidth) + relativePositionMain; + node->layout.position[leading[crossAxis]] = + YGNodeLeadingMargin(node, crossAxis, parentWidth) + relativePositionCross; + node->layout.position[trailing[crossAxis]] = + YGNodeTrailingMargin(node, crossAxis, parentWidth) + relativePositionCross; +} + +static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, + const YGNodeRef child, + const float width, + const YGMeasureMode widthMode, + const float height, + const float parentWidth, + const float parentHeight, + const YGMeasureMode heightMode, + const YGDirection direction, + const YGConfigRef config) { + const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const float mainAxisSize = isMainAxisRow ? width : height; + const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; + + float childWidth; + float childHeight; + YGMeasureMode childWidthMeasureMode; + YGMeasureMode childHeightMeasureMode; + + const float resolvedFlexBasis = + YGResolveValue(YGNodeResolveFlexBasisPtr(child), mainAxisParentSize); + const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, parentWidth); + const bool isColumnStyleDimDefined = + YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, parentHeight); + + if (!YGFloatIsUndefined(resolvedFlexBasis) && !YGFloatIsUndefined(mainAxisSize)) { + if (YGFloatIsUndefined(child->layout.computedFlexBasis) || + (YGConfigIsExperimentalFeatureEnabled(child->config, YGExperimentalFeatureWebFlexBasis) && + child->layout.computedFlexBasisGeneration != gCurrentGenerationCount)) { + child->layout.computedFlexBasis = + fmaxf(resolvedFlexBasis, YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth)); + } + } else if (isMainAxisRow && isRowStyleDimDefined) { + // The width is definite, so use that as the flex basis. + child->layout.computedFlexBasis = + fmaxf(YGResolveValue(child->resolvedDimensions[YGDimensionWidth], parentWidth), + YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth)); + } else if (!isMainAxisRow && isColumnStyleDimDefined) { + // The height is definite, so use that as the flex basis. + child->layout.computedFlexBasis = + fmaxf(YGResolveValue(child->resolvedDimensions[YGDimensionHeight], parentHeight), + YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, parentWidth)); + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped + // flex basis). + childWidth = YGUndefined; + childHeight = YGUndefined; + childWidthMeasureMode = YGMeasureModeUndefined; + childHeightMeasureMode = YGMeasureModeUndefined; + + const float marginRow = YGNodeMarginForAxis(child, YGFlexDirectionRow, parentWidth); + const float marginColumn = YGNodeMarginForAxis(child, YGFlexDirectionColumn, parentWidth); + + if (isRowStyleDimDefined) { + childWidth = + YGResolveValue(child->resolvedDimensions[YGDimensionWidth], parentWidth) + marginRow; + childWidthMeasureMode = YGMeasureModeExactly; + } + if (isColumnStyleDimDefined) { + childHeight = + YGResolveValue(child->resolvedDimensions[YGDimensionHeight], parentHeight) + marginColumn; + childHeightMeasureMode = YGMeasureModeExactly; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if ((!isMainAxisRow && node->style.overflow == YGOverflowScroll) || + node->style.overflow != YGOverflowScroll) { + if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + } + + if ((isMainAxisRow && node->style.overflow == YGOverflowScroll) || + node->style.overflow != YGOverflowScroll) { + if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) { + childHeight = height; + childHeightMeasureMode = YGMeasureModeAtMost; + } + } + + if (!YGFloatIsUndefined(child->style.aspectRatio)) { + if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) { + childHeight = (childWidth - marginRow) / child->style.aspectRatio; + childHeightMeasureMode = YGMeasureModeExactly; + } else if (isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) { + childWidth = (childHeight - marginColumn) * child->style.aspectRatio; + childWidthMeasureMode = YGMeasureModeExactly; + } + } + + // 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 + + const bool hasExactWidth = !YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly; + const bool childWidthStretch = YGNodeAlignItem(node, child) == YGAlignStretch && + childWidthMeasureMode != YGMeasureModeExactly; + if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && childWidthStretch) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeExactly; + if (!YGFloatIsUndefined(child->style.aspectRatio)) { + childHeight = (childWidth - marginRow) / child->style.aspectRatio; + childHeightMeasureMode = YGMeasureModeExactly; + } + } + + const bool hasExactHeight = !YGFloatIsUndefined(height) && heightMode == YGMeasureModeExactly; + const bool childHeightStretch = YGNodeAlignItem(node, child) == YGAlignStretch && + childHeightMeasureMode != YGMeasureModeExactly; + if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && childHeightStretch) { + childHeight = height; + childHeightMeasureMode = YGMeasureModeExactly; + + if (!YGFloatIsUndefined(child->style.aspectRatio)) { + childWidth = (childHeight - marginColumn) * child->style.aspectRatio; + childWidthMeasureMode = YGMeasureModeExactly; + } + } + + YGConstrainMaxSizeForMode( + child, YGFlexDirectionRow, parentWidth, parentWidth, &childWidthMeasureMode, &childWidth); + YGConstrainMaxSizeForMode(child, + YGFlexDirectionColumn, + parentHeight, + parentWidth, + &childHeightMeasureMode, + &childHeight); + + // Measure the child + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + parentWidth, + parentHeight, + false, + "measure", + config); + + child->layout.computedFlexBasis = + fmaxf(child->layout.measuredDimensions[dim[mainAxis]], + YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth)); + } + + child->layout.computedFlexBasisGeneration = gCurrentGenerationCount; +} + +static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, + const YGNodeRef child, + const float width, + const YGMeasureMode widthMode, + const float height, + const YGDirection direction, + const YGConfigRef config) { + const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction); + const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined; + YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined; + + const float marginRow = YGNodeMarginForAxis(child, YGFlexDirectionRow, width); + const float marginColumn = YGNodeMarginForAxis(child, YGFlexDirectionColumn, width); + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) { + childWidth = YGResolveValue(child->resolvedDimensions[YGDimensionWidth], width) + marginRow; + } else { + // If the child doesn't have a specified width, compute the width based + // on the left/right + // offsets if they're defined. + if (YGNodeIsLeadingPosDefined(child, YGFlexDirectionRow) && + YGNodeIsTrailingPosDefined(child, YGFlexDirectionRow)) { + childWidth = node->layout.measuredDimensions[YGDimensionWidth] - + (YGNodeLeadingBorder(node, YGFlexDirectionRow) + + YGNodeTrailingBorder(node, YGFlexDirectionRow)) - + (YGNodeLeadingPosition(child, YGFlexDirectionRow, width) + + YGNodeTrailingPosition(child, YGFlexDirectionRow, width)); + childWidth = YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width); + } + } + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) { + childHeight = + YGResolveValue(child->resolvedDimensions[YGDimensionHeight], height) + marginColumn; + } else { + // If the child doesn't have a specified height, compute the height + // based on the top/bottom + // offsets if they're defined. + if (YGNodeIsLeadingPosDefined(child, YGFlexDirectionColumn) && + YGNodeIsTrailingPosDefined(child, YGFlexDirectionColumn)) { + childHeight = node->layout.measuredDimensions[YGDimensionHeight] - + (YGNodeLeadingBorder(node, YGFlexDirectionColumn) + + YGNodeTrailingBorder(node, YGFlexDirectionColumn)) - + (YGNodeLeadingPosition(child, YGFlexDirectionColumn, height) + + YGNodeTrailingPosition(child, YGFlexDirectionColumn, height)); + childHeight = YGNodeBoundAxis(child, YGFlexDirectionColumn, childHeight, height, width); + } + } + + // Exactly one dimension needs to be defined for us to be able to do aspect ratio + // calculation. One dimension being the anchor and the other being flexible. + if (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) { + if (!YGFloatIsUndefined(child->style.aspectRatio)) { + if (YGFloatIsUndefined(childWidth)) { + childWidth = marginRow + (childHeight - marginColumn) * child->style.aspectRatio; + } else if (YGFloatIsUndefined(childHeight)) { + childHeight = marginColumn + (childWidth - marginRow) / child->style.aspectRatio; + } + } + } + + // If we're still missing one or the other dimension, measure the content. + if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) { + childWidthMeasureMode = + YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly; + childHeightMeasureMode = + YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly; + + // If the size of the parent is defined then try to constrain the absolute child to that size + // as well. This allows text within the absolute child to wrap to the size of its parent. + // This is the same behavior as many browsers implement. + if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && widthMode != YGMeasureModeUndefined && + width > 0) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + childWidth, + childHeight, + false, + "abs-measure", + config); + childWidth = child->layout.measuredDimensions[YGDimensionWidth] + + YGNodeMarginForAxis(child, YGFlexDirectionRow, width); + childHeight = child->layout.measuredDimensions[YGDimensionHeight] + + YGNodeMarginForAxis(child, YGFlexDirectionColumn, width); + } + + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + YGMeasureModeExactly, + YGMeasureModeExactly, + childWidth, + childHeight, + true, + "abs-layout", + config); + + if (YGNodeIsTrailingPosDefined(child, mainAxis) && !YGNodeIsLeadingPosDefined(child, mainAxis)) { + child->layout.position[leading[mainAxis]] = + node->layout.measuredDimensions[dim[mainAxis]] - + child->layout.measuredDimensions[dim[mainAxis]] - YGNodeTrailingBorder(node, mainAxis) - + YGNodeTrailingMargin(child, mainAxis, width) - + YGNodeTrailingPosition(child, mainAxis, isMainAxisRow ? width : height); + } else if (!YGNodeIsLeadingPosDefined(child, mainAxis) && + node->style.justifyContent == YGJustifyCenter) { + child->layout.position[leading[mainAxis]] = (node->layout.measuredDimensions[dim[mainAxis]] - + child->layout.measuredDimensions[dim[mainAxis]]) / + 2.0f; + } else if (!YGNodeIsLeadingPosDefined(child, mainAxis) && + node->style.justifyContent == YGJustifyFlexEnd) { + child->layout.position[leading[mainAxis]] = (node->layout.measuredDimensions[dim[mainAxis]] - + child->layout.measuredDimensions[dim[mainAxis]]); + } + + if (YGNodeIsTrailingPosDefined(child, crossAxis) && + !YGNodeIsLeadingPosDefined(child, crossAxis)) { + child->layout.position[leading[crossAxis]] = + node->layout.measuredDimensions[dim[crossAxis]] - + child->layout.measuredDimensions[dim[crossAxis]] - YGNodeTrailingBorder(node, crossAxis) - + YGNodeTrailingMargin(child, crossAxis, width) - + YGNodeTrailingPosition(child, crossAxis, isMainAxisRow ? height : width); + } else if (!YGNodeIsLeadingPosDefined(child, crossAxis) && + YGNodeAlignItem(node, child) == YGAlignCenter) { + child->layout.position[leading[crossAxis]] = + (node->layout.measuredDimensions[dim[crossAxis]] - + child->layout.measuredDimensions[dim[crossAxis]]) / + 2.0f; + } else if (!YGNodeIsLeadingPosDefined(child, crossAxis) && + ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^ + (node->style.flexWrap == YGWrapWrapReverse))) { + child->layout.position[leading[crossAxis]] = (node->layout.measuredDimensions[dim[crossAxis]] - + child->layout.measuredDimensions[dim[crossAxis]]); + } +} + +static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight) { + YGAssertWithNode( + node, + node->measure != nullptr, + "Expected node to have custom measure function"); + + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth); + const float paddingAndBorderAxisColumn = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, availableWidth); + const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, availableWidth); + const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, availableWidth); + + // We want to make sure we don't call measure with negative size + const float innerWidth = YGFloatIsUndefined(availableWidth) + ? availableWidth + : fmaxf(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + const float innerHeight = + YGFloatIsUndefined(availableHeight) + ? availableHeight + : fmaxf(0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); + + if (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly) { + // Don't bother sizing the text if both dimensions are already defined. + node->layout.measuredDimensions[YGDimensionWidth] = YGNodeBoundAxis( + node, YGFlexDirectionRow, availableWidth - marginAxisRow, parentWidth, parentWidth); + node->layout.measuredDimensions[YGDimensionHeight] = YGNodeBoundAxis( + node, YGFlexDirectionColumn, availableHeight - marginAxisColumn, parentHeight, parentWidth); + } else { + // Measure the text under the current constraints. + const YGSize measuredSize = + node->measure(node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode); + + node->layout.measuredDimensions[YGDimensionWidth] = + YGNodeBoundAxis(node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? measuredSize.width + paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + parentWidth, + parentWidth); + node->layout.measuredDimensions[YGDimensionHeight] = + YGNodeBoundAxis(node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? measuredSize.height + paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + parentHeight, + parentWidth); + } +} + +// 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. +static void YGNodeEmptyContainerSetMeasuredDimensions(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight) { + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, parentWidth); + const float paddingAndBorderAxisColumn = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, parentWidth); + const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); + const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); + + node->layout.measuredDimensions[YGDimensionWidth] = + YGNodeBoundAxis(node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + parentWidth, + parentWidth); + node->layout.measuredDimensions[YGDimensionHeight] = + YGNodeBoundAxis(node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + parentHeight, + parentWidth); +} + +static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight) { + if ((widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || + (heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || + (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly)) { + const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); + const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); + + node->layout.measuredDimensions[YGDimensionWidth] = + YGNodeBoundAxis(node, + YGFlexDirectionRow, + YGFloatIsUndefined(availableWidth) || + (widthMeasureMode == YGMeasureModeAtMost && availableWidth < 0.0f) + ? 0.0f + : availableWidth - marginAxisRow, + parentWidth, + parentWidth); + + node->layout.measuredDimensions[YGDimensionHeight] = + YGNodeBoundAxis(node, + YGFlexDirectionColumn, + YGFloatIsUndefined(availableHeight) || + (heightMeasureMode == YGMeasureModeAtMost && availableHeight < 0.0f) + ? 0.0f + : availableHeight - marginAxisColumn, + parentHeight, + parentWidth); + + return true; + } + + return false; +} + +static void YGZeroOutLayoutRecursivly(const YGNodeRef node) { + memset(&(node->layout), 0, sizeof(YGLayout)); + node->hasNewLayout = true; + YGCloneChildrenIfNeeded(node); + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = node->children[i]; + YGZeroOutLayoutRecursivly(child); + } +} + +// +// This is the main routine that implements a subset of the flexbox layout +// algorithm +// described in the W3C YG documentation: https://www.w3.org/TR/YG3-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. +// * 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 +// or YGUndefined if the size is not available; interpretation depends on +// layout +// 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 +// 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 +// 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/YG3-sizing/#terms): +// - YGMeasureModeUndefined: max content +// - YGMeasureModeExactly: fill available +// - YGMeasureModeAtMost: fit content +// +// When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller passes +// an available size of +// undefined then it must also pass a measure mode of YGMeasureModeUndefined +// in that dimension. +// +static void YGNodelayoutImpl(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight, + const bool performLayout, + const YGConfigRef config) { + YGAssertWithNode(node, + YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined + : true, + "availableWidth is indefinite so widthMeasureMode must be " + "YGMeasureModeUndefined"); + YGAssertWithNode(node, + YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined + : true, + "availableHeight is indefinite so heightMeasureMode must be " + "YGMeasureModeUndefined"); + + // Set the resolved resolution in the node's layout. + const YGDirection direction = YGNodeResolveDirection(node, parentDirection); + node->layout.direction = direction; + + const YGFlexDirection flexRowDirection = YGResolveFlexDirection(YGFlexDirectionRow, direction); + const YGFlexDirection flexColumnDirection = + YGResolveFlexDirection(YGFlexDirectionColumn, direction); + + node->layout.margin[YGEdgeStart] = YGNodeLeadingMargin(node, flexRowDirection, parentWidth); + node->layout.margin[YGEdgeEnd] = YGNodeTrailingMargin(node, flexRowDirection, parentWidth); + node->layout.margin[YGEdgeTop] = YGNodeLeadingMargin(node, flexColumnDirection, parentWidth); + node->layout.margin[YGEdgeBottom] = YGNodeTrailingMargin(node, flexColumnDirection, parentWidth); + + node->layout.border[YGEdgeStart] = YGNodeLeadingBorder(node, flexRowDirection); + node->layout.border[YGEdgeEnd] = YGNodeTrailingBorder(node, flexRowDirection); + node->layout.border[YGEdgeTop] = YGNodeLeadingBorder(node, flexColumnDirection); + node->layout.border[YGEdgeBottom] = YGNodeTrailingBorder(node, flexColumnDirection); + + node->layout.padding[YGEdgeStart] = YGNodeLeadingPadding(node, flexRowDirection, parentWidth); + node->layout.padding[YGEdgeEnd] = YGNodeTrailingPadding(node, flexRowDirection, parentWidth); + node->layout.padding[YGEdgeTop] = YGNodeLeadingPadding(node, flexColumnDirection, parentWidth); + node->layout.padding[YGEdgeBottom] = + YGNodeTrailingPadding(node, flexColumnDirection, parentWidth); + + if (node->measure) { + YGNodeWithMeasureFuncSetMeasuredDimensions(node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight); + return; + } + + const uint32_t childCount = node->children.size(); + if (childCount == 0) { + YGNodeEmptyContainerSetMeasuredDimensions(node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight); + return; + } + + // If we're not being asked to perform a full layout we can skip the algorithm if we already know + // the size + if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions(node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight)) { + return; + } + + // At this point we know we're going to perform work. Ensure that each child has a mutable copy. + YGCloneChildrenIfNeeded(node); + + // Reset layout flags, as they could have changed. + node->layout.hadOverflow = false; + + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction); + const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const YGJustify justifyContent = node->style.justifyContent; + const bool isNodeFlexWrap = node->style.flexWrap != YGWrapNoWrap; + + const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; + const float crossAxisParentSize = isMainAxisRow ? parentHeight : parentWidth; + + YGNodeRef firstAbsoluteChild = nullptr; + YGNodeRef currentAbsoluteChild = nullptr; + + const float leadingPaddingAndBorderMain = + YGNodeLeadingPaddingAndBorder(node, mainAxis, parentWidth); + const float trailingPaddingAndBorderMain = + YGNodeTrailingPaddingAndBorder(node, mainAxis, parentWidth); + const float leadingPaddingAndBorderCross = + YGNodeLeadingPaddingAndBorder(node, crossAxis, parentWidth); + const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, parentWidth); + const float paddingAndBorderAxisCross = + YGNodePaddingAndBorderForAxis(node, crossAxis, parentWidth); + + YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + YGMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + const float paddingAndBorderAxisRow = + isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross; + const float paddingAndBorderAxisColumn = + isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; + + const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); + const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + const float minInnerWidth = + YGResolveValue(&node->style.minDimensions[YGDimensionWidth], parentWidth) - + paddingAndBorderAxisRow; + const float maxInnerWidth = + YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth) - + paddingAndBorderAxisRow; + const float minInnerHeight = + YGResolveValue(&node->style.minDimensions[YGDimensionHeight], parentHeight) - + paddingAndBorderAxisColumn; + const float maxInnerHeight = + YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight) - + paddingAndBorderAxisColumn; + const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight; + const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight; + + // Max dimension overrides predefined dimension value; Min dimension in turn overrides both of the + // above + float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + if (!YGFloatIsUndefined(availableInnerWidth)) { + // We want to make sure our available width does not violate min and max constraints + availableInnerWidth = fmaxf(fminf(availableInnerWidth, maxInnerWidth), minInnerWidth); + } + + float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + if (!YGFloatIsUndefined(availableInnerHeight)) { + // We want to make sure our available height does not violate min and max constraints + availableInnerHeight = fmaxf(fminf(availableInnerHeight, maxInnerHeight), minInnerHeight); + } + + float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + const float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // If there is only one child with flexGrow + flexShrink it means we can set the + // computedFlexBasis to 0 instead of measuring and shrinking / flexing the child to exactly + // match the remaining space + YGNodeRef singleFlexChild = nullptr; + if (measureModeMainDim == YGMeasureModeExactly) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (singleFlexChild) { + if (YGNodeIsFlex(child)) { + // There is already a flexible child, abort. + singleFlexChild = nullptr; + break; + } + } else if (YGResolveFlexGrow(child) > 0.0f && YGNodeResolveFlexShrink(child) > 0.0f) { + singleFlexChild = child; + } + } + } + + float totalOuterFlexBasis = 0; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = node->children[i]; + if (child->style.display == YGDisplayNone) { + YGZeroOutLayoutRecursivly(child); + child->hasNewLayout = true; + child->isDirty = false; + continue; + } + YGResolveDimensions(child); + if (performLayout) { + // Set the initial position (relative to the parent). + const YGDirection childDirection = YGNodeResolveDirection(child, direction); + YGNodeSetPosition(child, + childDirection, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (child->style.positionType == YGPositionTypeAbsolute) { + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == nullptr) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != nullptr) { + currentAbsoluteChild->nextChild = child; + } + currentAbsoluteChild = child; + child->nextChild = nullptr; + } else { + if (child == singleFlexChild) { + child->layout.computedFlexBasisGeneration = gCurrentGenerationCount; + child->layout.computedFlexBasis = 0; + } else { + YGNodeComputeFlexBasisForChild(node, + child, + availableInnerWidth, + widthMeasureMode, + availableInnerHeight, + availableInnerWidth, + availableInnerHeight, + heightMeasureMode, + direction, + config); + } + } + + totalOuterFlexBasis += + child->layout.computedFlexBasis + YGNodeMarginForAxis(child, mainAxis, availableInnerWidth); + ; + } + + const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined + ? false + : totalOuterFlexBasis > availableInnerMainDim; + if (isNodeFlexWrap && flexBasisOverflows && measureModeMainDim == YGMeasureModeAtMost) { + measureModeMainDim = YGMeasureModeExactly; + } + + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + uint32_t startOfLineIndex = 0; + uint32_t endOfLineIndex = 0; + + // Number of lines. + uint32_t lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; + + // Max main dimension of all the lines. + float maxLineMainDim = 0; + + for (; endOfLineIndex < childCount; lineCount++, startOfLineIndex = endOfLineIndex) { + // 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. + uint32_t 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 sizeConsumedOnCurrentLineIncludingMinConstraint = 0; + + float totalFlexGrowFactors = 0; + float totalFlexShrinkScaledFactors = 0; + + // Maintain a linked list of the child nodes that can shrink and/or grow. + YGNodeRef firstRelativeChild = nullptr; + YGNodeRef currentRelativeChild = nullptr; + + // Add items to the current line until it's full or we run out of items. + for (uint32_t i = startOfLineIndex; i < childCount; i++, endOfLineIndex++) { + const YGNodeRef child = node->children[i]; + if (child->style.display == YGDisplayNone) { + continue; + } + child->lineIndex = lineCount; + + if (child->style.positionType != YGPositionTypeAbsolute) { + const float childMarginMainAxis = YGNodeMarginForAxis(child, mainAxis, availableInnerWidth); + const float flexBasisWithMaxConstraints = + fminf(YGResolveValue(&child->style.maxDimensions[dim[mainAxis]], mainAxisParentSize), + child->layout.computedFlexBasis); + const float flexBasisWithMinAndMaxConstraints = + fmaxf(YGResolveValue(&child->style.minDimensions[dim[mainAxis]], mainAxisParentSize), + flexBasisWithMaxConstraints); + + // 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 (sizeConsumedOnCurrentLineIncludingMinConstraint + flexBasisWithMinAndMaxConstraints + + childMarginMainAxis > + availableInnerMainDim && + isNodeFlexWrap && itemsOnLine > 0) { + break; + } + + sizeConsumedOnCurrentLineIncludingMinConstraint += + flexBasisWithMinAndMaxConstraints + childMarginMainAxis; + sizeConsumedOnCurrentLine += flexBasisWithMinAndMaxConstraints + childMarginMainAxis; + itemsOnLine++; + + if (YGNodeIsFlex(child)) { + totalFlexGrowFactors += YGResolveFlexGrow(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child dimension. + totalFlexShrinkScaledFactors += + -YGNodeResolveFlexShrink(child) * child->layout.computedFlexBasis; + } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild == nullptr) { + firstRelativeChild = child; + } + if (currentRelativeChild != nullptr) { + currentRelativeChild->nextChild = child; + } + currentRelativeChild = child; + child->nextChild = nullptr; + } + } + + // The total flex factor needs to be floored to 1. + if (totalFlexGrowFactors > 0 && totalFlexGrowFactors < 1) { + totalFlexGrowFactors = 1; + } + + // The total flex shrink factor needs to be floored to 1. + if (totalFlexShrinkScaledFactors > 0 && totalFlexShrinkScaledFactors < 1) { + totalFlexShrinkScaledFactors = 1; + } + + // If we don't need to measure the cross axis, we can skip the entire flex + // step. + const bool canSkipFlex = !performLayout && measureModeCrossDim == YGMeasureModeExactly; + + // 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. + + bool sizeBasedOnContent = false; + // If we don't measure with exact main dimension we want to ensure we don't violate min and max + if (measureModeMainDim != YGMeasureModeExactly) { + if (!YGFloatIsUndefined(minInnerMainDim) && sizeConsumedOnCurrentLine < minInnerMainDim) { + availableInnerMainDim = minInnerMainDim; + } else if (!YGFloatIsUndefined(maxInnerMainDim) && + sizeConsumedOnCurrentLine > maxInnerMainDim) { + availableInnerMainDim = maxInnerMainDim; + } else { + if (!node->config->useLegacyStretchBehaviour && + (totalFlexGrowFactors == 0 || YGResolveFlexGrow(node) == 0)) { + // If we don't have any children to flex or we can't flex the node itself, + // space we've used is all space we need. Root node also should be shrunk to minimum + availableInnerMainDim = sizeConsumedOnCurrentLine; + } + sizeBasedOnContent = !node->config->useLegacyStretchBehaviour; + } + } + + float remainingFreeSpace = 0; + if (!sizeBasedOnContent && !YGFloatIsUndefined(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 points for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + const 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/YG-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 != nullptr) { + childFlexBasis = + fminf(YGResolveValue(¤tRelativeChild->style.maxDimensions[dim[mainAxis]], + mainAxisParentSize), + fmaxf(YGResolveValue(¤tRelativeChild->style.minDimensions[dim[mainAxis]], + mainAxisParentSize), + currentRelativeChild->layout.computedFlexBasis)); + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = -YGNodeResolveFlexShrink(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + baseMainSize = + childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = YGNodeBoundAxis(currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + 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 = YGResolveFlexGrow(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + baseMainSize = + childFlexBasis + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = YGNodeBoundAxis(currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + + 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; + } + } + } + + currentRelativeChild = currentRelativeChild->nextChild; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + + // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != nullptr) { + childFlexBasis = + fminf(YGResolveValue(¤tRelativeChild->style.maxDimensions[dim[mainAxis]], + mainAxisParentSize), + fmaxf(YGResolveValue(¤tRelativeChild->style.minDimensions[dim[mainAxis]], + mainAxisParentSize), + currentRelativeChild->layout.computedFlexBasis)); + float updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = -YGNodeResolveFlexShrink(currentRelativeChild) * childFlexBasis; + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + float childSize; + + if (totalFlexShrinkScaledFactors == 0) { + childSize = childFlexBasis + flexShrinkScaledFactor; + } else { + childSize = + childFlexBasis + + (remainingFreeSpace / totalFlexShrinkScaledFactors) * flexShrinkScaledFactor; + } + + updatedMainSize = YGNodeBoundAxis(currentRelativeChild, + mainAxis, + childSize, + availableInnerMainDim, + availableInnerWidth); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = YGResolveFlexGrow(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + updatedMainSize = + YGNodeBoundAxis(currentRelativeChild, + mainAxis, + childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor, + availableInnerMainDim, + availableInnerWidth); + } + } + + deltaFreeSpace -= updatedMainSize - childFlexBasis; + + const float marginMain = + YGNodeMarginForAxis(currentRelativeChild, mainAxis, availableInnerWidth); + const float marginCross = + YGNodeMarginForAxis(currentRelativeChild, crossAxis, availableInnerWidth); + + float childCrossSize; + float childMainSize = updatedMainSize + marginMain; + YGMeasureMode childCrossMeasureMode; + YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; + + if (!YGFloatIsUndefined(currentRelativeChild->style.aspectRatio)) { + childCrossSize = + isMainAxisRow + ? (childMainSize - marginMain) / currentRelativeChild->style.aspectRatio + : (childMainSize - marginMain) * currentRelativeChild->style.aspectRatio; + childCrossMeasureMode = YGMeasureModeExactly; + + childCrossSize += marginCross; + } else if (!YGFloatIsUndefined(availableInnerCrossDim) && + !YGNodeIsStyleDimDefined(currentRelativeChild, + crossAxis, + availableInnerCrossDim) && + measureModeCrossDim == YGMeasureModeExactly && + !(isNodeFlexWrap && flexBasisOverflows) && + YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch && + YGMarginLeadingValue(currentRelativeChild, crossAxis)->unit != YGUnitAuto && + YGMarginTrailingValue(currentRelativeChild, crossAxis)->unit != YGUnitAuto) { + childCrossSize = availableInnerCrossDim; + childCrossMeasureMode = YGMeasureModeExactly; + } else if (!YGNodeIsStyleDimDefined(currentRelativeChild, + crossAxis, + availableInnerCrossDim)) { + childCrossSize = availableInnerCrossDim; + childCrossMeasureMode = + YGFloatIsUndefined(childCrossSize) ? YGMeasureModeUndefined : YGMeasureModeAtMost; + } else { + childCrossSize = YGResolveValue(currentRelativeChild->resolvedDimensions[dim[crossAxis]], + availableInnerCrossDim) + + marginCross; + const bool isLoosePercentageMeasurement = + currentRelativeChild->resolvedDimensions[dim[crossAxis]]->unit == YGUnitPercent && + measureModeCrossDim != YGMeasureModeExactly; + childCrossMeasureMode = YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + } + + YGConstrainMaxSizeForMode(currentRelativeChild, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize); + YGConstrainMaxSizeForMode(currentRelativeChild, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize); + + const bool requiresStretchLayout = + !YGNodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) && + YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch && + YGMarginLeadingValue(currentRelativeChild, crossAxis)->unit != YGUnitAuto && + YGMarginTrailingValue(currentRelativeChild, crossAxis)->unit != YGUnitAuto; + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const YGMeasureMode childWidthMeasureMode = + isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; + const YGMeasureMode childHeightMeasureMode = + !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; + + // Recursively call the layout algorithm for this child with the updated + // main size. + YGLayoutNodeInternal(currentRelativeChild, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + performLayout && !requiresStretchLayout, + "flex", + config); + node->layout.hadOverflow |= currentRelativeChild->layout.hadOverflow; + + currentRelativeChild = currentRelativeChild->nextChild; + } + } + + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; + node->layout.hadOverflow |= (remainingFreeSpace < 0); + + // 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. Calculate the remaining space when + // constraint by the min size defined for the main axis. + + if (measureModeMainDim == YGMeasureModeAtMost && remainingFreeSpace > 0) { + if (node->style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined && + YGResolveValue(&node->style.minDimensions[dim[mainAxis]], mainAxisParentSize) >= 0) { + remainingFreeSpace = + fmaxf(0, + YGResolveValue(&node->style.minDimensions[dim[mainAxis]], mainAxisParentSize) - + (availableInnerMainDim - remainingFreeSpace)); + } else { + remainingFreeSpace = 0; + } + } + + int numberOfAutoMarginsOnCurrentLine = 0; + for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { + const YGNodeRef child = node->children[i]; + if (child->style.positionType == YGPositionTypeRelative) { + if (YGMarginLeadingValue(child, mainAxis)->unit == YGUnitAuto) { + numberOfAutoMarginsOnCurrentLine++; + } + if (YGMarginTrailingValue(child, mainAxis)->unit == YGUnitAuto) { + numberOfAutoMarginsOnCurrentLine++; + } + } + } + + if (numberOfAutoMarginsOnCurrentLine == 0) { + switch (justifyContent) { + case YGJustifyCenter: + leadingMainDim = remainingFreeSpace / 2; + break; + case YGJustifyFlexEnd: + leadingMainDim = remainingFreeSpace; + break; + case YGJustifySpaceBetween: + if (itemsOnLine > 1) { + betweenMainDim = fmaxf(remainingFreeSpace, 0) / (itemsOnLine - 1); + } else { + betweenMainDim = 0; + } + break; + case YGJustifySpaceEvenly: + // Space is distributed evenly across all elements + betweenMainDim = remainingFreeSpace / (itemsOnLine + 1); + leadingMainDim = betweenMainDim; + break; + case YGJustifySpaceAround: + // Space on the edges is half of the space between elements + betweenMainDim = remainingFreeSpace / itemsOnLine; + leadingMainDim = betweenMainDim / 2; + break; + case YGJustifyFlexStart: + break; + } + } + + float mainDim = leadingPaddingAndBorderMain + leadingMainDim; + float crossDim = 0; + + for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { + const YGNodeRef child = node->children[i]; + if (child->style.display == YGDisplayNone) { + continue; + } + if (child->style.positionType == YGPositionTypeAbsolute && + YGNodeIsLeadingPosDefined(child, 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]] = + YGNodeLeadingPosition(child, mainAxis, availableInnerMainDim) + + YGNodeLeadingBorder(node, mainAxis) + + YGNodeLeadingMargin(child, mainAxis, availableInnerWidth); + } + } else { + // 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. + if (child->style.positionType == YGPositionTypeRelative) { + if (YGMarginLeadingValue(child, mainAxis)->unit == YGUnitAuto) { + mainDim += remainingFreeSpace / numberOfAutoMarginsOnCurrentLine; + } + + if (performLayout) { + child->layout.position[pos[mainAxis]] += mainDim; + } + + if (YGMarginTrailingValue(child, mainAxis)->unit == YGUnitAuto) { + mainDim += remainingFreeSpace / numberOfAutoMarginsOnCurrentLine; + } + + 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 YGNodeDimWithMargin. + mainDim += betweenMainDim + YGNodeMarginForAxis(child, mainAxis, availableInnerWidth) + + child->layout.computedFlexBasis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus the spacing. + mainDim += betweenMainDim + YGNodeDimWithMargin(child, mainAxis, availableInnerWidth); + + // The cross dimension is the max of the elements dimension since + // there can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); + } + } else if (performLayout) { + child->layout.position[pos[mainAxis]] += + YGNodeLeadingBorder(node, mainAxis) + leadingMainDim; + } + } + } + + mainDim += trailingPaddingAndBorderMain; + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == YGMeasureModeUndefined || + measureModeCrossDim == YGMeasureModeAtMost) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = YGNodeBoundAxis(node, + crossAxis, + crossDim + paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) - + paddingAndBorderAxisCross; + } + + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) { + crossDim = availableInnerCrossDim; + } + + // Clamp to the min/max size specified on the container. + crossDim = YGNodeBoundAxis(node, + crossAxis, + crossDim + paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) - + paddingAndBorderAxisCross; + + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { + const YGNodeRef child = node->children[i]; + if (child->style.display == YGDisplayNone) { + continue; + } + if (child->style.positionType == YGPositionTypeAbsolute) { + // If the child is absolutely positioned and has a + // top/left/bottom/right + // set, override all the previously computed positions to set it + // correctly. + const bool isChildLeadingPosDefined = YGNodeIsLeadingPosDefined(child, crossAxis); + if (isChildLeadingPosDefined) { + child->layout.position[pos[crossAxis]] = + YGNodeLeadingPosition(child, crossAxis, availableInnerCrossDim) + + YGNodeLeadingBorder(node, crossAxis) + + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); + } + // If leading position is not defined or calculations result in Nan, default to border + margin + if (!isChildLeadingPosDefined || YGFloatIsUndefined(child->layout.position[pos[crossAxis]])) { + child->layout.position[pos[crossAxis]] = + YGNodeLeadingBorder(node, crossAxis) + + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); + } + } 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 + const YGAlign alignItem = YGNodeAlignItem(node, child); + + // 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. + if (alignItem == YGAlignStretch && + YGMarginLeadingValue(child, crossAxis)->unit != YGUnitAuto && + YGMarginTrailingValue(child, crossAxis)->unit != YGUnitAuto) { + // If the child defines a definite size for its cross axis, there's + // no need to stretch. + if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) { + float childMainSize = child->layout.measuredDimensions[dim[mainAxis]]; + float childCrossSize = + !YGFloatIsUndefined(child->style.aspectRatio) + ? ((YGNodeMarginForAxis(child, crossAxis, availableInnerWidth) + + (isMainAxisRow ? childMainSize / child->style.aspectRatio + : childMainSize * child->style.aspectRatio))) + : crossDim; + + childMainSize += YGNodeMarginForAxis(child, mainAxis, availableInnerWidth); + + YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; + YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly; + YGConstrainMaxSizeForMode(child, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize); + YGConstrainMaxSizeForMode(child, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize); + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const YGMeasureMode childWidthMeasureMode = + YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly; + const YGMeasureMode childHeightMeasureMode = + YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly; + + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + true, + "stretch", + config); + } + } else { + const float remainingCrossDim = + containerCrossAxis - YGNodeDimWithMargin(child, crossAxis, availableInnerWidth); + + if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto && + YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) { + leadingCrossDim += fmaxf(0.0f, remainingCrossDim / 2); + } else if (YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) { + // No-Op + } else if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto) { + leadingCrossDim += fmaxf(0.0f, remainingCrossDim); + } else if (alignItem == YGAlignFlexStart) { + // No-Op + } else if (alignItem == YGAlignCenter) { + leadingCrossDim += remainingCrossDim / 2; + } else { + leadingCrossDim += remainingCrossDim; + } + } + // And we apply the position + child->layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; + } + } + } + + totalLineCrossDim += crossDim; + maxLineMainDim = fmaxf(maxLineMainDim, mainDim); + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) && + !YGFloatIsUndefined(availableInnerCrossDim)) { + const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; + + float crossDimLead = 0; + float currentLead = leadingPaddingAndBorderCross; + + switch (node->style.alignContent) { + case YGAlignFlexEnd: + currentLead += remainingAlignContentDim; + break; + case YGAlignCenter: + currentLead += remainingAlignContentDim / 2; + break; + case YGAlignStretch: + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = remainingAlignContentDim / lineCount; + } + break; + case YGAlignSpaceAround: + if (availableInnerCrossDim > totalLineCrossDim) { + currentLead += remainingAlignContentDim / (2 * lineCount); + if (lineCount > 1) { + crossDimLead = remainingAlignContentDim / lineCount; + } + } else { + currentLead += remainingAlignContentDim / 2; + } + break; + case YGAlignSpaceBetween: + if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) { + crossDimLead = remainingAlignContentDim / (lineCount - 1); + } + break; + case YGAlignAuto: + case YGAlignFlexStart: + case YGAlignBaseline: + break; + } + + uint32_t endIndex = 0; + for (uint32_t i = 0; i < lineCount; i++) { + const uint32_t startIndex = endIndex; + uint32_t ii; + + // compute the line's height and find the endIndex + float lineHeight = 0; + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + for (ii = startIndex; ii < childCount; ii++) { + const YGNodeRef child = node->children[ii]; + if (child->style.display == YGDisplayNone) { + continue; + } + if (child->style.positionType == YGPositionTypeRelative) { + if (child->lineIndex != i) { + break; + } + if (YGNodeIsLayoutDimDefined(child, crossAxis)) { + lineHeight = fmaxf(lineHeight, + child->layout.measuredDimensions[dim[crossAxis]] + + YGNodeMarginForAxis(child, crossAxis, availableInnerWidth)); + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + const float ascent = + YGBaseline(child) + + YGNodeLeadingMargin(child, YGFlexDirectionColumn, availableInnerWidth); + const float descent = + child->layout.measuredDimensions[YGDimensionHeight] + + YGNodeMarginForAxis(child, YGFlexDirectionColumn, availableInnerWidth) - ascent; + maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent); + lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + } + } + } + endIndex = ii; + lineHeight += crossDimLead; + + if (performLayout) { + for (ii = startIndex; ii < endIndex; ii++) { + const YGNodeRef child = node->children[ii]; + if (child->style.display == YGDisplayNone) { + continue; + } + if (child->style.positionType == YGPositionTypeRelative) { + switch (YGNodeAlignItem(node, child)) { + case YGAlignFlexStart: { + child->layout.position[pos[crossAxis]] = + currentLead + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); + break; + } + case YGAlignFlexEnd: { + child->layout.position[pos[crossAxis]] = + currentLead + lineHeight - + YGNodeTrailingMargin(child, crossAxis, availableInnerWidth) - + child->layout.measuredDimensions[dim[crossAxis]]; + break; + } + case YGAlignCenter: { + float childHeight = child->layout.measuredDimensions[dim[crossAxis]]; + child->layout.position[pos[crossAxis]] = + currentLead + (lineHeight - childHeight) / 2; + break; + } + case YGAlignStretch: { + child->layout.position[pos[crossAxis]] = + currentLead + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); + + // Remeasure child with the line height as it as been only measured with the + // parents height yet. + if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) { + const float childWidth = + isMainAxisRow ? (child->layout.measuredDimensions[YGDimensionWidth] + + YGNodeMarginForAxis(child, mainAxis, availableInnerWidth)) + : lineHeight; + + const float childHeight = + !isMainAxisRow ? (child->layout.measuredDimensions[YGDimensionHeight] + + YGNodeMarginForAxis(child, crossAxis, availableInnerWidth)) + : lineHeight; + + if (!(YGFloatsEqual(childWidth, + child->layout.measuredDimensions[YGDimensionWidth]) && + YGFloatsEqual(childHeight, + child->layout.measuredDimensions[YGDimensionHeight]))) { + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + YGMeasureModeExactly, + YGMeasureModeExactly, + availableInnerWidth, + availableInnerHeight, + true, + "multiline-stretch", + config); + } + } + break; + } + case YGAlignBaseline: { + child->layout.position[YGEdgeTop] = + currentLead + maxAscentForCurrentLine - YGBaseline(child) + + YGNodeLeadingPosition(child, YGFlexDirectionColumn, availableInnerCrossDim); + break; + } + case YGAlignAuto: + case YGAlignSpaceBetween: + case YGAlignSpaceAround: + break; + } + } + } + } + + currentLead += lineHeight; + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + node->layout.measuredDimensions[YGDimensionWidth] = YGNodeBoundAxis( + node, YGFlexDirectionRow, availableWidth - marginAxisRow, parentWidth, parentWidth); + node->layout.measuredDimensions[YGDimensionHeight] = YGNodeBoundAxis( + node, YGFlexDirectionColumn, availableHeight - marginAxisColumn, parentHeight, parentWidth); + + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == YGMeasureModeUndefined || + (node->style.overflow != YGOverflowScroll && measureModeMainDim == YGMeasureModeAtMost)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->layout.measuredDimensions[dim[mainAxis]] = + YGNodeBoundAxis(node, mainAxis, maxLineMainDim, mainAxisParentSize, parentWidth); + } else if (measureModeMainDim == YGMeasureModeAtMost && + node->style.overflow == YGOverflowScroll) { + node->layout.measuredDimensions[dim[mainAxis]] = fmaxf( + fminf(availableInnerMainDim + paddingAndBorderAxisMain, + YGNodeBoundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim, mainAxisParentSize)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim == YGMeasureModeUndefined || + (node->style.overflow != YGOverflowScroll && measureModeCrossDim == YGMeasureModeAtMost)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->layout.measuredDimensions[dim[crossAxis]] = + YGNodeBoundAxis(node, + crossAxis, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth); + } else if (measureModeCrossDim == YGMeasureModeAtMost && + node->style.overflow == YGOverflowScroll) { + node->layout.measuredDimensions[dim[crossAxis]] = + fmaxf(fminf(availableInnerCrossDim + paddingAndBorderAxisCross, + YGNodeBoundAxisWithinMinAndMax(node, + crossAxis, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisParentSize)), + paddingAndBorderAxisCross); + } + + // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse. + if (performLayout && node->style.flexWrap == YGWrapWrapReverse) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->style.positionType == YGPositionTypeRelative) { + child->layout.position[pos[crossAxis]] = node->layout.measuredDimensions[dim[crossAxis]] - + child->layout.position[pos[crossAxis]] - + child->layout.measuredDimensions[dim[crossAxis]]; + } + } + } + + if (performLayout) { + // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN + for (currentAbsoluteChild = firstAbsoluteChild; + currentAbsoluteChild != nullptr; + currentAbsoluteChild = currentAbsoluteChild->nextChild) { + YGNodeAbsoluteLayoutChild(node, + currentAbsoluteChild, + availableInnerWidth, + isMainAxisRow ? measureModeMainDim : measureModeCrossDim, + availableInnerHeight, + direction, + config); + } + + // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN + const bool needsMainTrailingPos = + mainAxis == YGFlexDirectionRowReverse || mainAxis == YGFlexDirectionColumnReverse; + const bool needsCrossTrailingPos = + crossAxis == YGFlexDirectionRowReverse || crossAxis == YGFlexDirectionColumnReverse; + + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = node->children[i]; + if (child->style.display == YGDisplayNone) { + continue; + } + if (needsMainTrailingPos) { + YGNodeSetChildTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + YGNodeSetChildTrailingPosition(node, child, crossAxis); + } + } + } + } +} + +uint32_t gDepth = 0; +bool gPrintTree = false; +bool gPrintChanges = false; +bool gPrintSkips = false; + +static const char *spacer = " "; + +static const char *YGSpacer(const unsigned long level) { + const size_t spacerLen = strlen(spacer); + if (level > spacerLen) { + return &spacer[0]; + } else { + return &spacer[spacerLen - level]; + } +} + +static const char *YGMeasureModeName(const YGMeasureMode mode, const bool performLayout) { + const char *kMeasureModeNames[YGMeasureModeCount] = {"UNDEFINED", "EXACTLY", "AT_MOST"}; + const char *kLayoutModeNames[YGMeasureModeCount] = {"LAY_UNDEFINED", + "LAY_EXACTLY", + "LAY_AT_" + "MOST"}; + + if (mode >= YGMeasureModeCount) { + return ""; + } + + return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode]; +} + +static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(YGMeasureMode sizeMode, + float size, + float lastComputedSize) { + return sizeMode == YGMeasureModeExactly && YGFloatsEqual(size, lastComputedSize); +} + +static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(YGMeasureMode sizeMode, + float size, + YGMeasureMode lastSizeMode, + float lastComputedSize) { + return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined && + (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize)); +} + +static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureMode sizeMode, + float size, + YGMeasureMode lastSizeMode, + float lastSize, + float lastComputedSize) { + return lastSizeMode == YGMeasureModeAtMost && sizeMode == YGMeasureModeAtMost && + lastSize > size && (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); +} + +float YGRoundValueToPixelGrid(const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + float scaledValue = value * pointScaleFactor; + float fractial = fmodf(scaledValue, 1.0); + if (YGFloatsEqual(fractial, 0)) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial; + } else if (YGFloatsEqual(fractial, 1.0)) { + scaledValue = scaledValue - fractial + 1.0; + } else if (forceCeil) { + // Next we check if we need to use forced rounding + scaledValue = scaledValue - fractial + 1.0f; + } else if (forceFloor) { + scaledValue = scaledValue - fractial; + } else { + // Finally we just round the value + scaledValue = scaledValue - fractial + + (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f) ? 1.0f : 0.0f); + } + return scaledValue / pointScaleFactor; +} + +bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, + const float width, + const YGMeasureMode heightMode, + const float height, + const YGMeasureMode lastWidthMode, + const float lastWidth, + const YGMeasureMode lastHeightMode, + const float lastHeight, + const float lastComputedWidth, + const float lastComputedHeight, + const float marginRow, + const float marginColumn, + const YGConfigRef config) { + if (lastComputedHeight < 0 || lastComputedWidth < 0) { + return false; + } + bool useRoundedComparison = + config != nullptr && config->pointScaleFactor != 0; + const float effectiveWidth = + useRoundedComparison ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false) + : width; + const float effectiveHeight = + useRoundedComparison ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false) + : height; + const float effectiveLastWidth = + useRoundedComparison + ? YGRoundValueToPixelGrid(lastWidth, config->pointScaleFactor, false, false) + : lastWidth; + const float effectiveLastHeight = + useRoundedComparison + ? YGRoundValueToPixelGrid(lastHeight, config->pointScaleFactor, false, false) + : lastHeight; + + const bool hasSameWidthSpec = + lastWidthMode == widthMode && YGFloatsEqual(effectiveLastWidth, effectiveWidth); + const bool hasSameHeightSpec = + lastHeightMode == heightMode && YGFloatsEqual(effectiveLastHeight, effectiveHeight); + + const bool widthIsCompatible = + hasSameWidthSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode, + width - marginRow, + lastComputedWidth) || + YGMeasureModeOldSizeIsUnspecifiedAndStillFits(widthMode, + width - marginRow, + lastWidthMode, + lastComputedWidth) || + YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth); + + const bool heightIsCompatible = + hasSameHeightSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode, + height - marginColumn, + lastComputedHeight) || + YGMeasureModeOldSizeIsUnspecifiedAndStillFits(heightMode, + height - marginColumn, + lastHeightMode, + lastComputedHeight) || + YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight); + + return widthIsCompatible && heightIsCompatible; +} + +// +// This is a wrapper around the YGNodelayoutImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as YGNodelayoutImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool YGLayoutNodeInternal(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight, + const bool performLayout, + const char *reason, + const YGConfigRef config) { + YGLayout *layout = &node->layout; + + gDepth++; + + const bool needToVisitNode = + (node->isDirty && layout->generationCount != gCurrentGenerationCount) || + layout->lastParentDirection != parentDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout->nextCachedMeasurementsIndex = 0; + layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1; + layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1; + layout->cachedLayout.computedWidth = -1; + layout->cachedLayout.computedHeight = -1; + } + + YGCachedMeasurement* cachedResults = nullptr; + + // 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 (node->measure) { + const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); + const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); + + // First, try to use the layout cache. + if (YGNodeCanUseCachedMeasurement(widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout->cachedLayout.widthMeasureMode, + layout->cachedLayout.availableWidth, + layout->cachedLayout.heightMeasureMode, + layout->cachedLayout.availableHeight, + layout->cachedLayout.computedWidth, + layout->cachedLayout.computedHeight, + marginAxisRow, + marginAxisColumn, + config)) { + cachedResults = &layout->cachedLayout; + } else { + // Try to use the measurement cache. + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (YGNodeCanUseCachedMeasurement(widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout->cachedMeasurements[i].widthMeasureMode, + layout->cachedMeasurements[i].availableWidth, + layout->cachedMeasurements[i].heightMeasureMode, + layout->cachedMeasurements[i].availableHeight, + layout->cachedMeasurements[i].computedWidth, + layout->cachedMeasurements[i].computedHeight, + marginAxisRow, + marginAxisColumn, + config)) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { + if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) && + YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) && + layout->cachedLayout.widthMeasureMode == widthMeasureMode && + layout->cachedLayout.heightMeasureMode == heightMeasureMode) { + cachedResults = &layout->cachedLayout; + } + } else { + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (YGFloatsEqual(layout->cachedMeasurements[i].availableWidth, availableWidth) && + YGFloatsEqual(layout->cachedMeasurements[i].availableHeight, availableHeight) && + layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode && + layout->cachedMeasurements[i].heightMeasureMode == heightMeasureMode) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != nullptr) { + layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth; + layout->measuredDimensions[YGDimensionHeight] = cachedResults->computedHeight; + + if (gPrintChanges && gPrintSkips) { + YGLog(node, YGLogLevelVerbose, "%s%d.{[skipped] ", YGSpacer(gDepth), gDepth); + if (node->print) { + node->print(node); + } + YGLog( + node, + YGLogLevelVerbose, + "wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + cachedResults->computedWidth, + cachedResults->computedHeight, + reason); + } + } else { + if (gPrintChanges) { + YGLog( + node, + YGLogLevelVerbose, + "%s%d.{%s", + YGSpacer(gDepth), + gDepth, + needToVisitNode ? "*" : ""); + if (node->print) { + node->print(node); + } + YGLog( + node, + YGLogLevelVerbose, + "wm: %s, hm: %s, aw: %f ah: %f %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + reason); + } + + YGNodelayoutImpl(node, + availableWidth, + availableHeight, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + performLayout, + config); + + if (gPrintChanges) { + YGLog( + node, + YGLogLevelVerbose, + "%s%d.}%s", + YGSpacer(gDepth), + gDepth, + needToVisitNode ? "*" : ""); + if (node->print) { + node->print(node); + } + YGLog( + node, + YGLogLevelVerbose, + "wm: %s, hm: %s, d: (%f, %f) %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + layout->measuredDimensions[YGDimensionWidth], + layout->measuredDimensions[YGDimensionHeight], + reason); + } + + layout->lastParentDirection = parentDirection; + + if (cachedResults == nullptr) { + if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) { + if (gPrintChanges) { + YGLog(node, YGLogLevelVerbose, "Out of cache entries!\n"); + } + layout->nextCachedMeasurementsIndex = 0; + } + + YGCachedMeasurement *newCacheEntry; + if (performLayout) { + // Use the single layout cache entry. + newCacheEntry = &layout->cachedLayout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex]; + layout->nextCachedMeasurementsIndex++; + } + + newCacheEntry->availableWidth = availableWidth; + newCacheEntry->availableHeight = availableHeight; + newCacheEntry->widthMeasureMode = widthMeasureMode; + newCacheEntry->heightMeasureMode = heightMeasureMode; + newCacheEntry->computedWidth = layout->measuredDimensions[YGDimensionWidth]; + newCacheEntry->computedHeight = layout->measuredDimensions[YGDimensionHeight]; + } + } + + if (performLayout) { + node->layout.dimensions[YGDimensionWidth] = node->layout.measuredDimensions[YGDimensionWidth]; + node->layout.dimensions[YGDimensionHeight] = node->layout.measuredDimensions[YGDimensionHeight]; + node->hasNewLayout = true; + node->isDirty = false; + } + + gDepth--; + layout->generationCount = gCurrentGenerationCount; + return (needToVisitNode || cachedResults == nullptr); +} + +void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint) { + YGAssertWithConfig(config, pixelsInPoint >= 0.0f, "Scale factor should not be less than zero"); + + // We store points for Pixel as we will use it for rounding + if (pixelsInPoint == 0.0f) { + // Zero is used to skip rounding + config->pointScaleFactor = 0.0f; + } else { + config->pointScaleFactor = pixelsInPoint; + } +} + +static void YGRoundToPixelGrid(const YGNodeRef node, + const float pointScaleFactor, + const float absoluteLeft, + const float absoluteTop) { + if (pointScaleFactor == 0.0f) { + return; + } + + const float nodeLeft = node->layout.position[YGEdgeLeft]; + const float nodeTop = node->layout.position[YGEdgeTop]; + + const float nodeWidth = node->layout.dimensions[YGDimensionWidth]; + const float nodeHeight = node->layout.dimensions[YGDimensionHeight]; + + const float absoluteNodeLeft = absoluteLeft + nodeLeft; + const float absoluteNodeTop = absoluteTop + nodeTop; + + const float absoluteNodeRight = absoluteNodeLeft + nodeWidth; + const float absoluteNodeBottom = absoluteNodeTop + nodeHeight; + + // If a node has a custom measure function we never want to round down its size as this could + // lead to unwanted text truncation. + const bool textRounding = node->nodeType == YGNodeTypeText; + + node->layout.position[YGEdgeLeft] = + YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding); + node->layout.position[YGEdgeTop] = + YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding); + + // We multiply dimension by scale factor and if the result is close to the whole number, we don't + // have any fraction + // To verify if the result is close to whole number we want to check both floor and ceil numbers + const bool hasFractionalWidth = !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) && + !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0); + const bool hasFractionalHeight = !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) && + !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0); + + node->layout.dimensions[YGDimensionWidth] = + YGRoundValueToPixelGrid(absoluteNodeRight, + pointScaleFactor, + (textRounding && hasFractionalWidth), + (textRounding && !hasFractionalWidth)) - + YGRoundValueToPixelGrid(absoluteNodeLeft, pointScaleFactor, false, textRounding); + node->layout.dimensions[YGDimensionHeight] = + YGRoundValueToPixelGrid(absoluteNodeBottom, + pointScaleFactor, + (textRounding && hasFractionalHeight), + (textRounding && !hasFractionalHeight)) - + YGRoundValueToPixelGrid(absoluteNodeTop, pointScaleFactor, false, textRounding); + + const uint32_t childCount = node->children.size(); + for (uint32_t i = 0; i < childCount; i++) { + YGRoundToPixelGrid(YGNodeGetChild(node, i), pointScaleFactor, absoluteNodeLeft, absoluteNodeTop); + } +} + +void YGNodeCalculateLayout(const YGNodeRef node, + const float parentWidth, + const float parentHeight, + const YGDirection parentDirection) { + // 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++; + + YGResolveDimensions(node); + + float width = YGUndefined; + YGMeasureMode widthMeasureMode = YGMeasureModeUndefined; + if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, parentWidth)) { + width = YGResolveValue(node->resolvedDimensions[dim[YGFlexDirectionRow]], parentWidth) + + YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); + widthMeasureMode = YGMeasureModeExactly; + } else if (YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth) >= 0.0f) { + width = YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth); + widthMeasureMode = YGMeasureModeAtMost; + } else { + width = parentWidth; + widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined : YGMeasureModeExactly; + } + + float height = YGUndefined; + YGMeasureMode heightMeasureMode = YGMeasureModeUndefined; + if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, parentHeight)) { + height = YGResolveValue(node->resolvedDimensions[dim[YGFlexDirectionColumn]], parentHeight) + + YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); + heightMeasureMode = YGMeasureModeExactly; + } else if (YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight) >= 0.0f) { + height = YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight); + heightMeasureMode = YGMeasureModeAtMost; + } else { + height = parentHeight; + heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined : YGMeasureModeExactly; + } + + if (YGLayoutNodeInternal(node, + width, + height, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + true, + "initial", + node->config)) { + YGNodeSetPosition(node, node->layout.direction, parentWidth, parentHeight, parentWidth); + YGRoundToPixelGrid(node, node->config->pointScaleFactor, 0.0f, 0.0f); + + if (gPrintTree) { + YGNodePrint( + node, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } + } +} + +void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { + if (logger != nullptr) { + config->logger = logger; + } else { +#ifdef ANDROID + config->logger = &YGAndroidLog; +#else + config->logger = &YGDefaultLog; +#endif + } +} + +static void YGVLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + const YGConfigRef logConfig = config != nullptr ? config : &gYGConfigDefaults; + logConfig->logger(logConfig, node, level, format, args); + + if (level == YGLogLevelFatal) { + abort(); + } +} + +void YGLogWithConfig(const YGConfigRef config, YGLogLevel level, const char *format, ...) { + va_list args; + va_start(args, format); + YGVLog(config, nullptr, level, format, args); + va_end(args); +} + +void YGLog(const YGNodeRef node, YGLogLevel level, const char *format, ...) { + va_list args; + va_start(args, format); + YGVLog(node == nullptr ? nullptr : node->config, node, level, format, args); + va_end(args); +} + +void YGAssert(const bool condition, const char *message) { + if (!condition) { + YGLog(nullptr, YGLogLevelFatal, "%s\n", message); + } +} + +void YGAssertWithNode(const YGNodeRef node, const bool condition, const char *message) { + if (!condition) { + YGLog(node, YGLogLevelFatal, "%s\n", message); + } +} + +void YGAssertWithConfig(const YGConfigRef config, const bool condition, const char *message) { + if (!condition) { + YGLogWithConfig(config, YGLogLevelFatal, "%s\n", message); + } +} + +void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled) { + config->experimentalFeatures[feature] = enabled; +} + +inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature) { + return config->experimentalFeatures[feature]; +} + +void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) { + config->useWebDefaults = enabled; +} + +void YGConfigSetUseLegacyStretchBehaviour(const YGConfigRef config, + const bool useLegacyStretchBehaviour) { + config->useLegacyStretchBehaviour = useLegacyStretchBehaviour; +} + +bool YGConfigGetUseWebDefaults(const YGConfigRef config) { + return config->useWebDefaults; +} + +void YGConfigSetContext(const YGConfigRef config, void *context) { + config->context = context; +} + +void *YGConfigGetContext(const YGConfigRef config) { + return config->context; +} + +void YGConfigSetNodeClonedFunc(const YGConfigRef config, const YGNodeClonedFunc callback) { + config->cloneNodeCallback = callback; +}
yoga/Yoga.h view
@@ -1,247 +1,283 @@-/**- * Copyright (c) 2014-present, Facebook, Inc.- * All rights reserved.- *- * This source code is licensed under the BSD-style license found in the- * LICENSE file in the root directory of this source tree. An additional grant- * of patent rights can be found in the PATENTS file in the same directory.- */--#pragma once--#include <assert.h>-#include <math.h>-#include <stdarg.h>-#include <stdint.h>-#include <stdio.h>-#include <stdlib.h>--#ifndef __cplusplus-#include <stdbool.h>-#endif--// Not defined in MSVC++-#ifndef NAN-static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff};-#define NAN (*(const float *) __nan)-#endif--#define YGUndefined NAN--#include "YGEnums.h"-#include "YGMacros.h"--YG_EXTERN_C_BEGIN--typedef struct YGSize {- float width;- float height;-} YGSize;--typedef struct YGValue {- float value;- YGUnit unit;-} YGValue;--static const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined};-static const YGValue YGValueAuto = {YGUndefined, YGUnitAuto};--typedef struct YGConfig *YGConfigRef;-typedef struct YGNode *YGNodeRef;-typedef YGSize (*YGMeasureFunc)(YGNodeRef node,- float width,- YGMeasureMode widthMode,- float height,- YGMeasureMode heightMode);-typedef float (*YGBaselineFunc)(YGNodeRef node, const float width, const float height);-typedef void (*YGPrintFunc)(YGNodeRef node);-typedef int (*YGLogger)(YGLogLevel level, const char *format, va_list args);--typedef void *(*YGMalloc)(size_t size);-typedef void *(*YGCalloc)(size_t count, size_t size);-typedef void *(*YGRealloc)(void *ptr, size_t size);-typedef void (*YGFree)(void *ptr);--// YGNode-WIN_EXPORT YGNodeRef YGNodeNew(void);-WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config);-WIN_EXPORT void YGNodeFree(const YGNodeRef node);-WIN_EXPORT void YGNodeFreeRecursive(const YGNodeRef node);-WIN_EXPORT void YGNodeReset(const YGNodeRef node);-WIN_EXPORT int32_t YGNodeGetInstanceCount(void);--WIN_EXPORT void YGNodeInsertChild(const YGNodeRef node,- const YGNodeRef child,- const uint32_t index);-WIN_EXPORT void YGNodeRemoveChild(const YGNodeRef node, const YGNodeRef child);-WIN_EXPORT YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index);-WIN_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node);-WIN_EXPORT uint32_t YGNodeGetChildCount(const YGNodeRef node);--WIN_EXPORT void YGNodeCalculateLayout(const YGNodeRef node,- const float availableWidth,- const float availableHeight,- const YGDirection parentDirection);--// Mark a node as dirty. Only valid for nodes with a custom measure function-// set.-// YG knows when to mark all other nodes as dirty but because nodes with-// measure functions-// depends on information not known to YG they must perform this dirty-// marking manually.-WIN_EXPORT void YGNodeMarkDirty(const YGNodeRef node);-WIN_EXPORT bool YGNodeIsDirty(const YGNodeRef node);--WIN_EXPORT void YGNodePrint(const YGNodeRef node, const YGPrintOptions options);--WIN_EXPORT bool YGFloatIsUndefined(const float value);--WIN_EXPORT bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode,- const float width,- const YGMeasureMode heightMode,- const float height,- const YGMeasureMode lastWidthMode,- const float lastWidth,- const YGMeasureMode lastHeightMode,- const float lastHeight,- const float lastComputedWidth,- const float lastComputedHeight,- const float marginRow,- const float marginColumn);--WIN_EXPORT void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode);--#define YG_NODE_PROPERTY(type, name, paramName) \- WIN_EXPORT void YGNodeSet##name(const YGNodeRef node, type paramName); \- WIN_EXPORT type YGNodeGet##name(const YGNodeRef node);--#define YG_NODE_STYLE_PROPERTY(type, name, paramName) \- WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, const type paramName); \- WIN_EXPORT type YGNodeStyleGet##name(const YGNodeRef node);--#define YG_NODE_STYLE_PROPERTY_UNIT(type, name, paramName) \- WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, const float paramName); \- WIN_EXPORT void YGNodeStyleSet##name##Percent(const YGNodeRef node, const float paramName); \- WIN_EXPORT type YGNodeStyleGet##name(const YGNodeRef node);--#define YG_NODE_STYLE_PROPERTY_UNIT_AUTO(type, name, paramName) \- YG_NODE_STYLE_PROPERTY_UNIT(type, name, paramName) \- WIN_EXPORT void YGNodeStyleSet##name##Auto(const YGNodeRef node);--#define YG_NODE_STYLE_EDGE_PROPERTY(type, name, paramName) \- WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, \- const YGEdge edge, \- const type paramName); \- WIN_EXPORT type YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge);--#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT(type, name, paramName) \- WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, \- const YGEdge edge, \- const float paramName); \- WIN_EXPORT void YGNodeStyleSet##name##Percent(const YGNodeRef node, \- const YGEdge edge, \- const float paramName); \- WIN_EXPORT WIN_STRUCT(type) YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge);--#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO(type, name) \- WIN_EXPORT void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge);--#define YG_NODE_LAYOUT_PROPERTY(type, name) \- WIN_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node);--#define YG_NODE_LAYOUT_EDGE_PROPERTY(type, name) \- WIN_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge);--YG_NODE_PROPERTY(void *, Context, context);-YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc);-YG_NODE_PROPERTY(YGBaselineFunc, BaselineFunc, baselineFunc)-YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc);-YG_NODE_PROPERTY(bool, HasNewLayout, hasNewLayout);--YG_NODE_STYLE_PROPERTY(YGDirection, Direction, direction);-YG_NODE_STYLE_PROPERTY(YGFlexDirection, FlexDirection, flexDirection);-YG_NODE_STYLE_PROPERTY(YGJustify, JustifyContent, justifyContent);-YG_NODE_STYLE_PROPERTY(YGAlign, AlignContent, alignContent);-YG_NODE_STYLE_PROPERTY(YGAlign, AlignItems, alignItems);-YG_NODE_STYLE_PROPERTY(YGAlign, AlignSelf, alignSelf);-YG_NODE_STYLE_PROPERTY(YGPositionType, PositionType, positionType);-YG_NODE_STYLE_PROPERTY(YGWrap, FlexWrap, flexWrap);-YG_NODE_STYLE_PROPERTY(YGOverflow, Overflow, overflow);-YG_NODE_STYLE_PROPERTY(YGDisplay, Display, display);--YG_NODE_STYLE_PROPERTY(float, Flex, flex);-YG_NODE_STYLE_PROPERTY(float, FlexGrow, flexGrow);-YG_NODE_STYLE_PROPERTY(float, FlexShrink, flexShrink);-YG_NODE_STYLE_PROPERTY_UNIT_AUTO(YGValue, FlexBasis, flexBasis);--YG_NODE_STYLE_EDGE_PROPERTY_UNIT(YGValue, Position, position);-YG_NODE_STYLE_EDGE_PROPERTY_UNIT(YGValue, Margin, margin);-YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO(YGValue, Margin);-YG_NODE_STYLE_EDGE_PROPERTY_UNIT(YGValue, Padding, padding);-YG_NODE_STYLE_EDGE_PROPERTY(float, Border, border);--YG_NODE_STYLE_PROPERTY_UNIT_AUTO(YGValue, Width, width);-YG_NODE_STYLE_PROPERTY_UNIT_AUTO(YGValue, Height, height);-YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MinWidth, minWidth);-YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MinHeight, minHeight);-YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MaxWidth, maxWidth);-YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MaxHeight, maxHeight);--// Yoga specific properties, not compatible with flexbox specification-// Aspect ratio control the size of the undefined dimension of a node.-// Aspect ratio is encoded as a floating point value width/height. e.g. A value of 2 leads to a node-// with a width twice the size of its height while a value of 0.5 gives the opposite effect.-//-// - On a node with a set width/height aspect ratio control the size of the unset dimension-// - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis if-// unset-// - On a node with a measure function aspect ratio works as though the measure function measures-// the flex basis-// - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis if-// unset-// - Aspect ratio takes min/max dimensions into account-YG_NODE_STYLE_PROPERTY(float, AspectRatio, aspectRatio);--YG_NODE_LAYOUT_PROPERTY(float, Left);-YG_NODE_LAYOUT_PROPERTY(float, Top);-YG_NODE_LAYOUT_PROPERTY(float, Right);-YG_NODE_LAYOUT_PROPERTY(float, Bottom);-YG_NODE_LAYOUT_PROPERTY(float, Width);-YG_NODE_LAYOUT_PROPERTY(float, Height);-YG_NODE_LAYOUT_PROPERTY(YGDirection, Direction);--// Get the computed values for these nodes after performing layout. If they were set using-// point values then the returned value will be the same as YGNodeStyleGetXXX. However if-// they were set using a percentage value then the returned value is the computed value used-// during layout.-YG_NODE_LAYOUT_EDGE_PROPERTY(float, Margin);-YG_NODE_LAYOUT_EDGE_PROPERTY(float, Border);-YG_NODE_LAYOUT_EDGE_PROPERTY(float, Padding);--WIN_EXPORT void YGSetLogger(YGLogger logger);-WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...);--// Set this to number of pixels in 1 point to round calculation results-// If you want to avoid rounding - set PointScaleFactor to 0-WIN_EXPORT void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint);--// YGConfig-WIN_EXPORT YGConfigRef YGConfigNew(void);-WIN_EXPORT void YGConfigFree(const YGConfigRef config);--WIN_EXPORT void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config,- const YGExperimentalFeature feature,- const bool enabled);-WIN_EXPORT bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config,- const YGExperimentalFeature feature);--// Using the web defaults is the prefered configuration for new projects.-// Usage of non web defaults should be considered as legacy.-WIN_EXPORT void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled);--WIN_EXPORT bool YGConfigGetUseWebDefaults(const YGConfigRef config);--WIN_EXPORT void-YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree);--YG_EXTERN_C_END+/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#include <assert.h> +#include <math.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef __cplusplus +#include <stdbool.h> +#endif + +// Not defined in MSVC++ +#ifndef NAN +static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; +#define NAN (*(const float *) __nan) +#endif + +#define YGUndefined NAN + +#include "YGEnums.h" +#include "YGMacros.h" + +YG_EXTERN_C_BEGIN + +typedef struct YGSize { + float width; + float height; +} YGSize; + +typedef struct YGValue { + float value; + YGUnit unit; +} YGValue; + +static const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; +static const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; + +typedef struct YGConfig *YGConfigRef; +typedef struct YGNode *YGNodeRef; +typedef YGSize (*YGMeasureFunc)(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode); +typedef float (*YGBaselineFunc)(YGNodeRef node, const float width, const float height); +typedef void (*YGPrintFunc)(YGNodeRef node); +typedef int (*YGLogger)(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args); +typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, + YGNodeRef newNode, + YGNodeRef parent, + int childIndex); + +// YGNode +WIN_EXPORT YGNodeRef YGNodeNew(void); +WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config); +WIN_EXPORT YGNodeRef YGNodeClone(const YGNodeRef node); +WIN_EXPORT void YGNodeFree(const YGNodeRef node); +WIN_EXPORT void YGNodeFreeRecursive(const YGNodeRef node); +WIN_EXPORT void YGNodeReset(const YGNodeRef node); +WIN_EXPORT int32_t YGNodeGetInstanceCount(void); + +WIN_EXPORT void YGNodeInsertChild(const YGNodeRef node, + const YGNodeRef child, + const uint32_t index); +WIN_EXPORT void YGNodeRemoveChild(const YGNodeRef node, const YGNodeRef child); +WIN_EXPORT void YGNodeRemoveAllChildren(const YGNodeRef node); +WIN_EXPORT YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index); +WIN_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node); +WIN_EXPORT uint32_t YGNodeGetChildCount(const YGNodeRef node); + +WIN_EXPORT void YGNodeCalculateLayout(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection); + +// Mark a node as dirty. Only valid for nodes with a custom measure function +// set. +// YG knows when to mark all other nodes as dirty but because nodes with +// measure functions +// depends on information not known to YG they must perform this dirty +// marking manually. +WIN_EXPORT void YGNodeMarkDirty(const YGNodeRef node); +WIN_EXPORT bool YGNodeIsDirty(const YGNodeRef node); + +WIN_EXPORT void YGNodePrint(const YGNodeRef node, const YGPrintOptions options); + +WIN_EXPORT bool YGFloatIsUndefined(const float value); + +WIN_EXPORT bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, + const float width, + const YGMeasureMode heightMode, + const float height, + const YGMeasureMode lastWidthMode, + const float lastWidth, + const YGMeasureMode lastHeightMode, + const float lastHeight, + const float lastComputedWidth, + const float lastComputedHeight, + const float marginRow, + const float marginColumn, + const YGConfigRef config); + +WIN_EXPORT void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode); + +#define YG_NODE_PROPERTY(type, name, paramName) \ + WIN_EXPORT void YGNodeSet##name(const YGNodeRef node, type paramName); \ + WIN_EXPORT type YGNodeGet##name(const YGNodeRef node); + +#define YG_NODE_STYLE_PROPERTY(type, name, paramName) \ + WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, const type paramName); \ + WIN_EXPORT type YGNodeStyleGet##name(const YGNodeRef node); + +#define YG_NODE_STYLE_PROPERTY_UNIT(type, name, paramName) \ + WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, const float paramName); \ + WIN_EXPORT void YGNodeStyleSet##name##Percent(const YGNodeRef node, const float paramName); \ + WIN_EXPORT type YGNodeStyleGet##name(const YGNodeRef node); + +#define YG_NODE_STYLE_PROPERTY_UNIT_AUTO(type, name, paramName) \ + YG_NODE_STYLE_PROPERTY_UNIT(type, name, paramName) \ + WIN_EXPORT void YGNodeStyleSet##name##Auto(const YGNodeRef node); + +#define YG_NODE_STYLE_EDGE_PROPERTY(type, name, paramName) \ + WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, \ + const YGEdge edge, \ + const type paramName); \ + WIN_EXPORT type YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge); + +#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT(type, name, paramName) \ + WIN_EXPORT void YGNodeStyleSet##name(const YGNodeRef node, \ + const YGEdge edge, \ + const float paramName); \ + WIN_EXPORT void YGNodeStyleSet##name##Percent(const YGNodeRef node, \ + const YGEdge edge, \ + const float paramName); \ + WIN_EXPORT WIN_STRUCT(type) YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge); + +#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO(type, name) \ + WIN_EXPORT void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge); + +#define YG_NODE_LAYOUT_PROPERTY(type, name) \ + WIN_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node); + +#define YG_NODE_LAYOUT_EDGE_PROPERTY(type, name) \ + WIN_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge); + +YG_NODE_PROPERTY(void *, Context, context); +YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc); +YG_NODE_PROPERTY(YGBaselineFunc, BaselineFunc, baselineFunc) +YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc); +YG_NODE_PROPERTY(bool, HasNewLayout, hasNewLayout); +YG_NODE_PROPERTY(YGNodeType, NodeType, nodeType); + +YG_NODE_STYLE_PROPERTY(YGDirection, Direction, direction); +YG_NODE_STYLE_PROPERTY(YGFlexDirection, FlexDirection, flexDirection); +YG_NODE_STYLE_PROPERTY(YGJustify, JustifyContent, justifyContent); +YG_NODE_STYLE_PROPERTY(YGAlign, AlignContent, alignContent); +YG_NODE_STYLE_PROPERTY(YGAlign, AlignItems, alignItems); +YG_NODE_STYLE_PROPERTY(YGAlign, AlignSelf, alignSelf); +YG_NODE_STYLE_PROPERTY(YGPositionType, PositionType, positionType); +YG_NODE_STYLE_PROPERTY(YGWrap, FlexWrap, flexWrap); +YG_NODE_STYLE_PROPERTY(YGOverflow, Overflow, overflow); +YG_NODE_STYLE_PROPERTY(YGDisplay, Display, display); + +YG_NODE_STYLE_PROPERTY(float, Flex, flex); +YG_NODE_STYLE_PROPERTY(float, FlexGrow, flexGrow); +YG_NODE_STYLE_PROPERTY(float, FlexShrink, flexShrink); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO(YGValue, FlexBasis, flexBasis); + +YG_NODE_STYLE_EDGE_PROPERTY_UNIT(YGValue, Position, position); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT(YGValue, Margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO(YGValue, Margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT(YGValue, Padding, padding); +YG_NODE_STYLE_EDGE_PROPERTY(float, Border, border); + +YG_NODE_STYLE_PROPERTY_UNIT_AUTO(YGValue, Width, width); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO(YGValue, Height, height); +YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MinWidth, minWidth); +YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MinHeight, minHeight); +YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MaxWidth, maxWidth); +YG_NODE_STYLE_PROPERTY_UNIT(YGValue, MaxHeight, maxHeight); + +// Yoga specific properties, not compatible with flexbox specification +// Aspect ratio control the size of the undefined dimension of a node. +// Aspect ratio is encoded as a floating point value width/height. e.g. A value of 2 leads to a node +// with a width twice the size of its height while a value of 0.5 gives the opposite effect. +// +// - On a node with a set width/height aspect ratio control the size of the unset dimension +// - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis if +// unset +// - On a node with a measure function aspect ratio works as though the measure function measures +// the flex basis +// - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis if +// unset +// - Aspect ratio takes min/max dimensions into account +YG_NODE_STYLE_PROPERTY(float, AspectRatio, aspectRatio); + +YG_NODE_LAYOUT_PROPERTY(float, Left); +YG_NODE_LAYOUT_PROPERTY(float, Top); +YG_NODE_LAYOUT_PROPERTY(float, Right); +YG_NODE_LAYOUT_PROPERTY(float, Bottom); +YG_NODE_LAYOUT_PROPERTY(float, Width); +YG_NODE_LAYOUT_PROPERTY(float, Height); +YG_NODE_LAYOUT_PROPERTY(YGDirection, Direction); +YG_NODE_LAYOUT_PROPERTY(bool, HadOverflow); + +// Get the computed values for these nodes after performing layout. If they were set using +// point values then the returned value will be the same as YGNodeStyleGetXXX. However if +// they were set using a percentage value then the returned value is the computed value used +// during layout. +YG_NODE_LAYOUT_EDGE_PROPERTY(float, Margin); +YG_NODE_LAYOUT_EDGE_PROPERTY(float, Border); +YG_NODE_LAYOUT_EDGE_PROPERTY(float, Padding); + +WIN_EXPORT void YGConfigSetLogger(const YGConfigRef config, YGLogger logger); +WIN_EXPORT void YGLog(const YGNodeRef node, YGLogLevel level, const char *message, ...); +WIN_EXPORT void YGLogWithConfig(const YGConfigRef config, YGLogLevel level, const char *format, ...); +WIN_EXPORT void YGAssert(const bool condition, const char *message); +WIN_EXPORT void YGAssertWithNode(const YGNodeRef node, const bool condition, const char *message); +WIN_EXPORT void YGAssertWithConfig(const YGConfigRef config, + const bool condition, + const char *message); + +// Set this to number of pixels in 1 point to round calculation results +// If you want to avoid rounding - set PointScaleFactor to 0 +WIN_EXPORT void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint); + +// Yoga previously had an error where containers would take the maximum space possible instead of +// the minimum +// like they are supposed to. In practice this resulted in implicit behaviour similar to align-self: +// stretch; +// Because this was such a long-standing bug we must allow legacy users to switch back to this +// behaviour. +WIN_EXPORT void YGConfigSetUseLegacyStretchBehaviour(const YGConfigRef config, + const bool useLegacyStretchBehaviour); + +// YGConfig +WIN_EXPORT YGConfigRef YGConfigNew(void); +WIN_EXPORT void YGConfigFree(const YGConfigRef config); +WIN_EXPORT void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src); +WIN_EXPORT int32_t YGConfigGetInstanceCount(void); + +WIN_EXPORT void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled); +WIN_EXPORT bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature); + +// Using the web defaults is the prefered configuration for new projects. +// Usage of non web defaults should be considered as legacy. +WIN_EXPORT void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled); +WIN_EXPORT bool YGConfigGetUseWebDefaults(const YGConfigRef config); + +WIN_EXPORT void YGConfigSetNodeClonedFunc(const YGConfigRef config, + const YGNodeClonedFunc callback); + +// Export only for C# +WIN_EXPORT YGConfigRef YGConfigGetDefault(void); + +WIN_EXPORT void YGConfigSetContext(const YGConfigRef config, void *context); +WIN_EXPORT void *YGConfigGetContext(const YGConfigRef config); + +WIN_EXPORT float YGRoundValueToPixelGrid( + const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor); + +YG_EXTERN_C_END