summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMokosha <>2017-12-14 16:41:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2017-12-14 16:41:00 (GMT)
commit521940ba7e04923a31f20ece583b938ae3f3a639 (patch)
tree44883815b0c36b564bda43d94f56c1471a539d9b
parentc45867c4f758a2f5086bed6b446701ec0f17591e (diff)
version 0.0.0.20.0.0.2
-rw-r--r--lib/Bindings/Yoga.hsc7
-rw-r--r--lib/Bindings/Yoga/Enums.hsc1
-rw-r--r--lib/Yoga.hs177
-rw-r--r--yoga.cabal16
-rw-r--r--yoga/YGEnums.cpp228
-rw-r--r--yoga/YGEnums.h292
-rw-r--r--yoga/YGMacros.h108
-rw-r--r--yoga/YGMemFuncs.h16
-rw-r--r--yoga/YGNodeList.c104
-rw-r--r--yoga/YGNodeList.h33
-rw-r--r--yoga/YGNodePrint.cpp220
-rw-r--r--yoga/YGNodePrint.h24
-rw-r--r--yoga/Yoga-internal.h230
-rw-r--r--yoga/Yoga.cpp (renamed from yoga/Yoga.c)7020
-rw-r--r--yoga/Yoga.h530
15 files changed, 4813 insertions, 4193 deletions
diff --git a/lib/Bindings/Yoga.hsc b/lib/Bindings/Yoga.hsc
index 9795291..983eb61 100644
--- a/lib/Bindings/Yoga.hsc
+++ b/lib/Bindings/Yoga.hsc
@@ -1,9 +1,6 @@
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE StandaloneDeriving #-}
-
-{-# OPTIONS_HADDOCK hide #-}
-
--------------------------------------------------------------------------------
#include <Yoga.h>
@@ -50,6 +47,7 @@ YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFr
-}
#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 @@ YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFr
#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 ()
diff --git a/lib/Bindings/Yoga/Enums.hsc b/lib/Bindings/Yoga/Enums.hsc
index 28dd52f..ab16e23 100644
--- a/lib/Bindings/Yoga/Enums.hsc
+++ b/lib/Bindings/Yoga/Enums.hsc
@@ -88,7 +88,6 @@ import Prelude (Num)
#num YGOverflowCount
-- typedef enum YGExperimentalFeature {
-#num YGExperimentalFeatureRounding
#num YGExperimentalFeatureWebFlexBasis
#num YGExperimentalFeatureCount
diff --git a/lib/Yoga.hs b/lib/Yoga.hs
index 308bcdf..a8ba69f 100644
--- a/lib/Yoga.hs
+++ b/lib/Yoga.hs
@@ -35,7 +35,7 @@ module Yoga (
-- ** Leaf nodes
Size(..),
- shrinkable, growable, exact,
+ shrinkable, growable, exact, withDimensions,
-- ** Attributes
Edge(..),
@@ -80,16 +80,13 @@ data Layout a
_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 @@ data Children a
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 @@ justifiedContainer just cs x = do
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 @@ setHeight (Range minHeight maxHeight) lyt =
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 @@ exact width height x = unsafePerformIO $ do
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 @@ layoutInfo ptr = do
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 @@ foldRenderTree parentInfo (Root x cs ptr) f = do
-- 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'
diff --git a/yoga.cabal b/yoga.cabal
index 8022406..273a1ef 100644
--- a/yoga.cabal
+++ b/yoga.cabal
@@ -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 @@ library
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 @@ library
build-depends:
base >= 4 && < 6,
ieee754 >= 0.7,
- bindings-DSL == 1.0.*
+ bindings-DSL
flag examples
description: Build examples
diff --git a/yoga/YGEnums.cpp b/yoga/YGEnums.cpp
new file mode 100644
index 0000000..7479c75
--- /dev/null
+++ b/yoga/YGEnums.cpp
@@ -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";
+}
diff --git a/yoga/YGEnums.h b/yoga/YGEnums.h
index 6022f55..113536c 100644
--- a/yoga/YGEnums.h
+++ b/yoga/YGEnums.h
@@ -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
diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h
index 2d8f728..48f257a 100644
--- a/yoga/YGMacros.h
+++ b/yoga/YGMacros.h
@@ -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
diff --git a/yoga/YGMemFuncs.h b/yoga/YGMemFuncs.h
deleted file mode 100644
index b19751b..0000000
--- a/yoga/YGMemFuncs.h
+++ /dev/null
@@ -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
diff --git a/yoga/YGNodeList.c b/yoga/YGNodeList.c
deleted file mode 100644
index d82753e..0000000
--- a/yoga/YGNodeList.c
+++ /dev/null
@@ -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;
-}
diff --git a/yoga/YGNodeList.h b/yoga/YGNodeList.h
deleted file mode 100644
index 41e272a..0000000
--- a/yoga/YGNodeList.h
+++ /dev/null
@@ -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
diff --git a/yoga/YGNodePrint.cpp b/yoga/YGNodePrint.cpp
new file mode 100644
index 0000000..38fd80e
--- /dev/null
+++ b/yoga/YGNodePrint.cpp
@@ -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
diff --git a/yoga/YGNodePrint.h b/yoga/YGNodePrint.h
new file mode 100644
index 0000000..6a9adb3
--- /dev/null
+++ b/yoga/YGNodePrint.h
@@ -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
diff --git a/yoga/Yoga-internal.h b/yoga/Yoga-internal.h
new file mode 100644
index 0000000..96c41a5
--- /dev/null
+++ b/yoga/Yoga-internal.h
@@ -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);
diff --git a/yoga/Yoga.c b/yoga/Yoga.cpp
index 52999d2..897b640 100644
--- a/yoga/Yoga.c
+++ b/yoga/Yoga.cpp
@@ -1,3487 +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 <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;
- }
-}
+/**
+ * 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(&currentRelativeChild->style.maxDimensions[dim[mainAxis]],
+ mainAxisParentSize),
+ fmaxf(YGResolveValue(&currentRelativeChild->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(&currentRelativeChild->style.maxDimensions[dim[mainAxis]],
+ mainAxisParentSize),
+ fmaxf(YGResolveValue(&currentRelativeChild->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;
+}
diff --git a/yoga/Yoga.h b/yoga/Yoga.h
index d5c5258..060e43a 100644
--- a/yoga/Yoga.h
+++ b/yoga/Yoga.h
@@ -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