summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMokosha <>2017-04-08 20:53:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2017-04-08 20:53:00 (GMT)
commitc45867c4f758a2f5086bed6b446701ec0f17591e (patch)
treec8a0fa5ee2218db53416aae5fc387d37d88468ba
version 0.0.0.10.0.0.1
-rw-r--r--LICENSE12
-rw-r--r--Setup.hs2
-rw-r--r--examples/Main.hs28
-rw-r--r--lib/Bindings/Yoga.hsc173
-rw-r--r--lib/Bindings/Yoga/Enums.hsc101
-rw-r--r--lib/Yoga.hs524
-rw-r--r--yoga.cabal85
-rw-r--r--yoga/YGEnums.h135
-rw-r--r--yoga/YGMacros.h61
-rw-r--r--yoga/YGMemFuncs.h16
-rw-r--r--yoga/YGNodeList.c104
-rw-r--r--yoga/YGNodeList.h33
-rw-r--r--yoga/Yoga.c3487
-rw-r--r--yoga/Yoga.h247
14 files changed, 5008 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2f366af
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,12 @@
+Copyright (c) 2016-present, Pavel Krajcevski
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/examples/Main.hs b/examples/Main.hs
new file mode 100644
index 0000000..5a52c0f
--- /dev/null
+++ b/examples/Main.hs
@@ -0,0 +1,28 @@
+module Main where
+
+--------------------------------------------------------------------------------
+import qualified Yoga as Yoga
+--------------------------------------------------------------------------------
+
+block :: Int -> Yoga.Layout Int
+block = Yoga.exact 100.0 100.0
+
+renderIntFn :: Yoga.LayoutInfo -> Int -> IO String
+renderIntFn info x = do
+ putStr $ concat ["Node ", show x, ": "]
+ print info
+ return ""
+
+main :: IO ()
+main = do
+ let cs = [ block y | y <- [6..9]]
+ cs2 = take 4 $ repeat $ Yoga.withPadding Yoga.Edge'Left 10.0 block 23
+ mkHbox = Yoga.hbox . Yoga.startToEnd
+ let tree =
+ flip Yoga.vbox 3 $
+ Yoga.startToEnd [
+ mkHbox cs 0,
+ ($ 1) (Yoga.withMargin Yoga.Edge'Top 10.0 $ mkHbox cs),
+ mkHbox cs2 2]
+ _ <- Yoga.render tree renderIntFn
+ return ()
diff --git a/lib/Bindings/Yoga.hsc b/lib/Bindings/Yoga.hsc
new file mode 100644
index 0000000..9795291
--- /dev/null
+++ b/lib/Bindings/Yoga.hsc
@@ -0,0 +1,173 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+{-# OPTIONS_HADDOCK hide #-}
+
+--------------------------------------------------------------------------------
+
+#include <Yoga.h>
+#include <bindings.dsl.h>
+
+--------------------------------------------------------------------------------
+
+module Bindings.Yoga where
+
+import Data.Data (Data)
+import Data.Typeable (Typeable)
+import Foreign.C.Types (CFloat(..), CInt(..), CUInt(..))
+import Foreign.Ptr (FunPtr, Ptr, plusPtr)
+import Foreign.Storable (Storable(..))
+
+import Prelude (Eq, IO, Show)
+import Prelude (($), return)
+--------------------------------------------------------------------------------
+
+#starttype struct YGSize
+#field width , CFloat
+#field height , CFloat
+#stoptype
+deriving instance Typeable C'YGSize
+
+#opaque_t YGNode
+deriving instance Typeable C'YGNode
+deriving instance Data C'YGNode
+
+#callback_t YGMeasureFunc, Ptr <YGNode> -> CFloat -> CInt -> CFloat -> CInt -> IO (Ptr <YGSize>)
+
+#callback_t YGPrintFunc, Ptr <YGNode> -> IO ()
+{--
+typedef int (*YGLogger)(YGLogLevel level, const char *format, va_list args);
+WIN_EXPORT void YGSetLogger(YGLogger logger);
+WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...);
+
+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 void
+YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree);
+-}
+
+#ccall YGNodeNew, IO (Ptr <YGNode>)
+#ccall YGNodeFree, Ptr <YGNode> -> IO ()
+#ccall YGNodeFreeRecursive, Ptr <YGNode> -> IO ()
+#ccall YGNodeReset, Ptr <YGNode> -> IO ()
+#ccall YGNodeGetInstanceCount, IO (CInt)
+
+#ccall YGNodeInsertChild, Ptr <YGNode> -> Ptr <YGNode> -> CUInt -> IO ()
+#ccall YGNodeRemoveChild, Ptr <YGNode> -> Ptr <YGNode> -> IO ()
+#ccall YGNodeGetChild, Ptr <YGNode> -> CUInt -> IO (Ptr <YGNode>)
+
+#ccall YGNodeCalculateLayout, Ptr <YGNode> -> CFloat -> CFloat -> CInt -> IO ()
+
+-- 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.
+
+#ccall YGNodeMarkDirty, Ptr <YGNode> -> IO ()
+#ccall YGNodeIsDirty, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodePrint, Ptr <YGNode> -> CInt -> IO ()
+
+#ccall YGNodeCanUseCachedMeasurement, CInt -> CFloat -> CInt -> CFloat -> CInt -> CFloat -> CInt -> CFloat -> CFloat -> CFloat -> CFloat -> CFloat -> IO CInt
+
+#ccall YGNodeCopyStyle, Ptr <YGNode> -> Ptr <YGNode> -> IO ()
+
+#ccall YGNodeSetContext, Ptr <YGNode> -> Ptr () -> IO ()
+#ccall YGNodeGetContext, Ptr <YGNode> -> IO (Ptr ())
+
+#ccall YGNodeSetMeasureFunc, Ptr <YGNode> -> Ptr <YGMeasureFunc> -> IO ()
+#ccall YGNodeGetMeasureFunc, Ptr <YGNode> -> IO (Ptr <YGMeasureFunc>)
+
+#ccall YGNodeSetPrintFunc, Ptr <YGNode> -> Ptr <YGPrintFunc> -> IO ()
+#ccall YGNodeGetPrintFunc, Ptr <YGNode> -> IO (Ptr <YGPrintFunc>)
+
+#ccall YGNodeSetHasNewLayout, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeGetHasNewLayout, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetDirection, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetDirection, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetFlexDirection, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetFlexDirection, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetJustifyContent, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetJustifyContent, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetAlignContent, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetAlignContent, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetAlignItems, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetAlignItems, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetAlignSelf, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetAlignSelf, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetPositionType, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetPositionType, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetFlexWrap, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetFlexWrap, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetOverflow, Ptr <YGNode> -> CInt -> IO ()
+#ccall YGNodeStyleGetOverflow, Ptr <YGNode> -> IO CInt
+
+#ccall YGNodeStyleSetFlex, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleSetFlexGrow, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetFlexGrow, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeStyleSetFlexShrink, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetFlexShrink, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeStyleSetFlexBasis, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetFlexBasis, Ptr <YGNode> -> IO CFloat
+
+#ccall YGNodeStyleSetPosition, Ptr <YGNode> -> CInt -> CFloat -> IO ()
+#ccall YGNodeStyleGetPosition, Ptr <YGNode> -> CInt -> IO CFloat
+
+#ccall YGNodeStyleSetMargin, Ptr <YGNode> -> CInt -> CFloat -> IO ()
+#ccall YGNodeStyleGetMargin, Ptr <YGNode> -> CInt -> IO CFloat
+
+#ccall YGNodeStyleSetPadding, Ptr <YGNode> -> CInt -> CFloat -> IO ()
+#ccall YGNodeStyleGetPadding, Ptr <YGNode> -> CInt -> IO CFloat
+
+#ccall YGNodeStyleSetBorder, Ptr <YGNode> -> CInt -> CFloat -> IO ()
+#ccall YGNodeStyleGetBorder, Ptr <YGNode> -> CInt -> IO CFloat
+
+#ccall YGNodeStyleSetWidth, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetWidth, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeStyleSetHeight, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetHeight, Ptr <YGNode> -> IO CFloat
+
+#ccall YGNodeStyleSetMinWidth, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetMinWidth, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeStyleSetMinHeight, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetMinHeight, Ptr <YGNode> -> IO CFloat
+
+#ccall YGNodeStyleSetMaxWidth, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetMaxWidth, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeStyleSetMaxHeight, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetMaxHeight, Ptr <YGNode> -> IO CFloat
+
+-- Yoga specific properties, not compatible with flexbox specification
+-- Aspect ratio control the size of the undefined dimension of a node.
+-- - 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
+#ccall YGNodeStyleSetAspectRatio, Ptr <YGNode> -> CFloat -> IO ()
+#ccall YGNodeStyleGetAspectRatio, Ptr <YGNode> -> IO CFloat
+
+#ccall YGNodeLayoutGetLeft, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeLayoutGetTop, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeLayoutGetRight, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeLayoutGetBottom, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeLayoutGetWidth, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeLayoutGetHeight, Ptr <YGNode> -> IO CFloat
+#ccall YGNodeLayoutGetDirection, Ptr <YGNode> -> IO CInt
diff --git a/lib/Bindings/Yoga/Enums.hsc b/lib/Bindings/Yoga/Enums.hsc
new file mode 100644
index 0000000..28dd52f
--- /dev/null
+++ b/lib/Bindings/Yoga/Enums.hsc
@@ -0,0 +1,101 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+--------------------------------------------------------------------------------
+
+#include <Yoga.h>
+#include <bindings.dsl.h>
+
+--------------------------------------------------------------------------------
+
+module Bindings.Yoga.Enums where
+
+import Prelude (Num)
+
+-- typedef enum YGFlexDirection
+#num YGFlexDirectionColumn
+#num YGFlexDirectionColumnReverse
+#num YGFlexDirectionRow
+#num YGFlexDirectionRowReverse
+#num YGFlexDirectionCount
+
+-- typedef enum YGMeasureMode {
+#num YGMeasureModeUndefined
+#num YGMeasureModeExactly
+#num YGMeasureModeAtMost
+#num YGMeasureModeCount
+
+-- typedef enum YGPrintOptions {
+#num YGPrintOptionsLayout
+#num YGPrintOptionsStyle
+#num YGPrintOptionsChildren
+#num YGPrintOptionsCount
+
+-- typedef enum YGEdge {
+#num YGEdgeLeft
+#num YGEdgeTop
+#num YGEdgeRight
+#num YGEdgeBottom
+#num YGEdgeStart
+#num YGEdgeEnd
+#num YGEdgeHorizontal
+#num YGEdgeVertical
+#num YGEdgeAll
+#num YGEdgeCount
+
+-- typedef enum YGPositionType {
+#num YGPositionTypeRelative
+#num YGPositionTypeAbsolute
+#num YGPositionTypeCount
+
+-- typedef enum YGDimension {
+#num YGDimensionWidth
+#num YGDimensionHeight
+#num YGDimensionCount
+
+-- typedef enum YGJustify {
+#num YGJustifyFlexStart
+#num YGJustifyCenter
+#num YGJustifyFlexEnd
+#num YGJustifySpaceBetween
+#num YGJustifySpaceAround
+#num YGJustifyCount
+
+-- typedef enum YGDirection {
+#num YGDirectionInherit
+#num YGDirectionLTR
+#num YGDirectionRTL
+#num YGDirectionCount
+
+-- typedef enum YGLogLevel {
+#num YGLogLevelError
+#num YGLogLevelWarn
+#num YGLogLevelInfo
+#num YGLogLevelDebug
+#num YGLogLevelVerbose
+#num YGLogLevelCount
+
+-- typedef enum YGWrap {
+#num YGWrapNoWrap
+#num YGWrapWrap
+#num YGWrapCount
+
+-- typedef enum YGOverflow {
+#num YGOverflowVisible
+#num YGOverflowHidden
+#num YGOverflowScroll
+#num YGOverflowCount
+
+-- typedef enum YGExperimentalFeature {
+#num YGExperimentalFeatureRounding
+#num YGExperimentalFeatureWebFlexBasis
+#num YGExperimentalFeatureCount
+
+-- typedef enum YGAlign {
+#num YGAlignAuto
+#num YGAlignFlexStart
+#num YGAlignCenter
+#num YGAlignFlexEnd
+#num YGAlignStretch
+#num YGAlignCount
diff --git a/lib/Yoga.hs b/lib/Yoga.hs
new file mode 100644
index 0000000..308bcdf
--- /dev/null
+++ b/lib/Yoga.hs
@@ -0,0 +1,524 @@
+{-|
+Module : Yoga
+Description : Bindings to Facebook's Yoga layout engine
+Copyright : (c) Pavel Krajcevski, 2017
+License : MIT
+Maintainer : Krajcevski@gmail.com
+Stability : experimental
+Portability : POSIX
+
+This module holds a high-level interface to the bindings associated with this
+library that are maintained in "Bindings.Yoga". Application developers will
+likely want to use this module to interface with the library, but are available
+to use the C-level bindings if more control is desired.
+
+These bindings are not affiliated with Facebook in any way, and have been
+developed separately for the sole purpose of interfacing with their open source
+library.
+
+Full documentation can be found at <http://facebook.github.io/yoga>
+-}
+module Yoga (
+ -- ** Main datatype
+ Layout,
+
+ -- ** Children layouts
+ -- | These layouts describe the way that children are ordered and spaced
+ -- within their parent.
+ Children, startToEnd, endToStart, centered, spaceBetween, spaceAround,
+ wrapped,
+
+ -- ** Containers
+ hbox, vbox,
+ hboxLeftToRight, hboxRightToLeft,
+ vboxTopToBottom, vboxBottomToTop,
+
+ -- ** Leaf nodes
+ Size(..),
+ shrinkable, growable, exact,
+
+ -- ** Attributes
+ Edge(..),
+ stretched, withMargin, withPadding,
+
+ -- ** Rendering
+ LayoutInfo(..), RenderFn, render, foldRender,
+
+) where
+--------------------------------------------------------------------------------
+import Bindings.Yoga
+import Bindings.Yoga.Enums
+
+import Control.Applicative
+import Control.Monad hiding (mapM, forM_)
+
+import Data.Foldable
+import Data.Traversable
+import Data.Monoid
+
+import Foreign.C.Types (CFloat, CInt)
+import Foreign.ForeignPtr
+
+import GHC.Ptr (Ptr)
+
+import Numeric.IEEE
+
+import System.IO.Unsafe
+
+-- Last to avoid compiler warnings due to the Foldable/Traversable Proposal
+import Prelude hiding (foldl, foldr, mapM)
+--------------------------------------------------------------------------------
+
+-- | The main datatype in the high level bindings is a 'Layout'. Layouts are
+-- used to store a tree of nodes that represent the different components of a
+-- layout. Layouts can be composed by adding one as a sub-tree to another. The
+-- parent-child relationship dictates different parameters such as width, height
+-- and position. This type is opaque to the user in order to facilitate updates
+-- to the library. For more control, use the C-level bindings in Yoga.Bindings.
+data Layout a
+ = Root { _payload :: 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 }
+ 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
+
+-- | Children are a list of layouts annotated with a style for how they should
+-- be laid out in their container.
+data Children a
+ = StartToEnd [Layout a]
+ | EndToStart [Layout a]
+ | Centered [Layout a]
+ | SpaceBetween [Layout a]
+ | SpaceAround [Layout a]
+ | Wrap (Children a)
+ deriving (Show, Eq, Ord)
+
+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
+
+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
+
+ 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
+
+ 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
+
+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
+
+ 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
+
+mkNode :: a -> IO (Layout a)
+mkNode x = do
+ ptr <- c'YGNodeNew
+ Root x [] <$> newForeignPtr p'YGNodeFree ptr
+
+-- | Collects a list of layouts and orients them from start to end depending
+-- on the orientation of the parent (LTR vs RTL).
+startToEnd :: [Layout a] -> Children a
+startToEnd = StartToEnd
+
+-- | Collects a list of layouts and orients them from end to start depending
+-- on the orientation of the parent (LTR vs RTL).
+endToStart :: [Layout a] -> Children a
+endToStart = EndToStart
+
+-- | Collects a list of children and centers them within the parent.
+centered :: [Layout a] -> Children a
+centered = Centered
+
+-- | Collects a list of children that span the parent such that the space
+-- between each child is equal
+spaceBetween :: [Layout a] -> Children a
+spaceBetween = SpaceBetween
+
+-- | Collects a list of children that span the parent such that the space to the
+-- left and right of each child is equal.
+spaceAround :: [Layout a] -> Children a
+spaceAround = SpaceAround
+
+-- | Permits children to wrap to a new line if they exceed the bounds of their
+-- parent
+wrapped :: Children a -> Children a
+wrapped (Wrap xs) = Wrap xs
+wrapped childs = childs
+
+justifiedContainer :: CInt -> [Layout a] -> a -> IO (Layout a)
+justifiedContainer just cs x = do
+ ptr <- c'YGNodeNew
+ 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!"
+ Root x cs' <$> newForeignPtr p'YGNodeFreeRecursive ptr
+
+assembleChildren :: Children a -> a -> IO (Layout a)
+assembleChildren (StartToEnd cs) x = justifiedContainer c'YGJustifyFlexStart cs x
+assembleChildren (EndToStart cs) x = justifiedContainer c'YGJustifyFlexEnd cs x
+assembleChildren (Centered cs) x = justifiedContainer c'YGJustifyCenter cs x
+assembleChildren (SpaceBetween cs) x =
+ justifiedContainer c'YGJustifySpaceBetween cs x
+assembleChildren (SpaceAround cs) x =
+ justifiedContainer c'YGJustifySpaceAround cs x
+assembleChildren (Wrap cs) x = assembleChildren cs x >>= wrapContainer
+ where
+ wrapContainer :: Layout a -> IO (Layout a)
+ wrapContainer lyt = do
+ withNativePtr lyt $ \ptr ->
+ c'YGNodeStyleSetFlexWrap ptr c'YGWrapWrap
+ return lyt
+
+setContainerDirection :: CInt -> CInt -> Layout a -> IO ()
+setContainerDirection dir flexDir lyt =
+ withNativePtr lyt $ \ptr -> do
+ c'YGNodeStyleSetDirection ptr dir
+ c'YGNodeStyleSetFlexDirection ptr flexDir
+
+-- | Generates a layout from a group of children and a payload such that the
+-- children are laid out horizontally. The orientation (RTL vs LTR) is
+-- inherited from the parent
+hbox :: Children a -> a -> Layout a
+hbox cs x = unsafePerformIO $ do
+ node <- assembleChildren cs x
+ setContainerDirection c'YGDirectionInherit c'YGFlexDirectionRow node
+ return node
+
+-- | Generates a layout from a group of children and a payload such that the
+-- children are laid out vertically. The orientation (top to bottom vs bottom to
+-- top) is inherited from the parent.
+vbox :: Children a -> a -> Layout a
+vbox cs x = unsafePerformIO $ do
+ node <- assembleChildren cs x
+ setContainerDirection c'YGDirectionInherit c'YGFlexDirectionColumn node
+ return node
+
+-- | Generates a layout from a group of children and a payload such that the
+-- children are laid out horizontally from left to right.
+hboxLeftToRight :: Children a -> a -> Layout a
+hboxLeftToRight cs x = unsafePerformIO $ do
+ node <- assembleChildren cs x
+ setContainerDirection c'YGDirectionLTR c'YGFlexDirectionRow node
+ return node
+
+-- | Generates a layout from a group of children and a payload such that the
+-- children are laid out horizontally from right to left.
+hboxRightToLeft :: Children a -> a -> Layout a
+hboxRightToLeft cs x = unsafePerformIO $ do
+ node <- assembleChildren cs x
+ setContainerDirection c'YGDirectionRTL c'YGFlexDirectionRow node
+ return node
+
+-- | Generates a layout from a group of children and a payload such that the
+-- children are laid out vertically from top to bottom.
+vboxTopToBottom :: Children a -> a -> Layout a
+vboxTopToBottom cs x = unsafePerformIO $ do
+ node <- assembleChildren cs x
+ setContainerDirection c'YGDirectionLTR c'YGFlexDirectionColumn node
+ return node
+
+-- | Generates a layout from a group of children and a payload such that the
+-- children are laid out vertically from bottom to top.
+vboxBottomToTop :: Children a -> a -> Layout a
+vboxBottomToTop cs x = unsafePerformIO $ do
+ node <- assembleChildren cs x
+ setContainerDirection c'YGDirectionRTL c'YGFlexDirectionColumn node
+ return node
+
+-- | A 'Size' is used to set properties about given layouts. In general, the
+-- width and height of a node along with its position are laid out by Yoga's
+-- internal layout engine. However, the user may decide to set limits on how
+-- much internal nodes can shrink or grow. This datatype controls those
+-- properties
+data Size
+ = Exact Float
+ | Min Float
+ | Max Float
+ | Range Float Float
+ deriving (Read, Show, Eq, Ord)
+
+setWidth :: Size -> Layout a -> IO ()
+setWidth (Exact w) lyt =
+ withNativePtr lyt $ \ptr -> c'YGNodeStyleSetWidth ptr $ realToFrac w
+setWidth (Min w) lyt =
+ withNativePtr lyt $ \ptr -> c'YGNodeStyleSetMinWidth ptr $ realToFrac w
+setWidth (Max w) lyt =
+ withNativePtr lyt $ \ptr -> c'YGNodeStyleSetMaxWidth ptr $ realToFrac w
+setWidth (Range minWidth maxWidth) lyt =
+ withNativePtr lyt $ \ptr -> do
+ c'YGNodeStyleSetMinWidth ptr $ realToFrac minWidth
+ c'YGNodeStyleSetMaxWidth ptr $ realToFrac maxWidth
+
+setHeight :: Size -> Layout a -> IO ()
+setHeight (Exact h) lyt =
+ withNativePtr lyt $ \ptr -> c'YGNodeStyleSetHeight ptr $ realToFrac h
+setHeight (Min h) lyt =
+ withNativePtr lyt $ \ptr -> c'YGNodeStyleSetMinHeight ptr $ realToFrac h
+setHeight (Max h) lyt =
+ withNativePtr lyt $ \ptr -> c'YGNodeStyleSetMaxHeight ptr $ realToFrac h
+setHeight (Range minHeight maxHeight) lyt =
+ withNativePtr lyt $ \ptr -> do
+ c'YGNodeStyleSetMinHeight ptr $ realToFrac minHeight
+ c'YGNodeStyleSetMaxHeight ptr $ realToFrac maxHeight
+
+-- | Creates a layout that 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
+ 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
+-- 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
+ setWidth width n
+ setHeight height n
+ withNativePtr n $ \ptr -> c'YGNodeStyleSetFlexGrow ptr $ realToFrac weight
+ return n
+
+-- | Creates a layout with the exact width and height for the given payload.
+exact :: Float -> Float -> a -> Layout a
+exact width height x = unsafePerformIO $ do
+ n <- mkNode 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 =
+ let node = mkNodeFn x
+ in unsafePerformIO $ do
+ withNativePtr node $ \ptr -> c'YGNodeStyleSetAlignSelf ptr c'YGAlignStretch
+ return node
+
+-- | Edges are used to describe the direction from which we want to alter an
+-- attribute of a node. They are currently only being used with 'withMargin' and
+-- 'withPadding'.
+data Edge
+ = Edge'Left
+ | Edge'Top
+ | Edge'Right
+ | Edge'Bottom
+ | Edge'Start
+ | Edge'End
+ | Edge'Horizontal
+ | Edge'Vertical
+ | Edge'All
+ deriving (Eq, Ord, Bounded, Enum, Read, Show)
+
+setMargin :: CInt -> Float -> Layout a -> IO (Layout a)
+setMargin edge px node = do
+ withNativePtr node $ \ptr -> c'YGNodeStyleSetMargin ptr edge $ realToFrac px
+ return node
+
+-- | Transforms a layout generator to one which applies the given margin using
+-- continuation passing style. In this way we maintain the const-ness of layout
+-- nodes. E.g.:
+--
+-- > let lyt = ($ payload) (withMargin Edge'Left 10.0 $ exact 200.0 300.0)
+withMargin :: Edge -> Float -> (b -> Layout a) -> b -> Layout a
+withMargin Edge'Left px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeLeft px (mkNodeFn x)
+withMargin Edge'Top px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeTop px (mkNodeFn x)
+withMargin Edge'Right px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeRight px (mkNodeFn x)
+withMargin Edge'Bottom px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeBottom px (mkNodeFn x)
+withMargin Edge'Start px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeStart px (mkNodeFn x)
+withMargin Edge'End px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeEnd px (mkNodeFn x)
+withMargin Edge'Horizontal px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeHorizontal px (mkNodeFn x)
+withMargin Edge'Vertical px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeVertical px (mkNodeFn x)
+withMargin Edge'All px mkNodeFn x =
+ unsafePerformIO $ setMargin c'YGEdgeAll px (mkNodeFn x)
+
+setPadding :: CInt -> Float -> Layout a -> IO (Layout a)
+setPadding edge px node = do
+ withNativePtr node $ \ptr ->
+ c'YGNodeStyleSetPadding ptr edge $ realToFrac px
+ return node
+
+-- | Transforms a layout generator to one which applies the given padding using
+-- continuation passing style. In this way we maintain the const-ness of layout
+-- nodes. E.g.:
+--
+-- > let lyt = ($ payload) (withPadding Edge'Left 10.0 $ exact 200.0 300.0)
+withPadding :: Edge -> Float -> (b -> Layout a) -> b -> Layout a
+withPadding Edge'Left px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeLeft px (mkNodeFn x)
+withPadding Edge'Top px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeTop px (mkNodeFn x)
+withPadding Edge'Right px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeRight px (mkNodeFn x)
+withPadding Edge'Bottom px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeBottom px (mkNodeFn x)
+withPadding Edge'Start px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeStart px (mkNodeFn x)
+withPadding Edge'End px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeEnd px (mkNodeFn x)
+withPadding Edge'Horizontal px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeHorizontal px (mkNodeFn x)
+withPadding Edge'Vertical px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeVertical px (mkNodeFn x)
+withPadding Edge'All px mkNodeFn x =
+ unsafePerformIO $ setPadding c'YGEdgeAll px (mkNodeFn x)
+
+--------------------------------------------------------------------------------
+-- Rendering
+
+-- | Stores the calculated layout information for a given node. During
+-- rendering, the rendering function will take the payload and layout info to
+-- facilitate the renderer to do whatever it needs to with the given layout
+-- calculations.
+data LayoutInfo = LayoutInfo {
+ nodeTop :: Float, -- ^ The y-coordinate of this node
+ nodeLeft :: Float, -- ^ The x-coordinate of this node
+ nodeWidth :: Float, -- ^ The width of this node
+ nodeHeight :: Float -- ^ The height of this node
+} deriving (Eq, Show)
+
+emptyInfo :: LayoutInfo
+emptyInfo = LayoutInfo 0 0 0 0
+
+layoutWithParent :: LayoutInfo -> LayoutInfo -> LayoutInfo
+layoutWithParent parent child =
+ let (x, y) = (nodeLeft parent, nodeTop parent)
+ (x', y') = (nodeLeft child, nodeTop child)
+ in child { nodeLeft = x + x', nodeTop = y + y' }
+
+-- | A 'RenderFn' takes a top-left position and a width and height of a node
+-- with the given payload. The function is expected to perform some monadic
+-- action in the 'Monad' m, and return a new payload of type b. This function is
+-- called on each node in order during a call to render.
+type RenderFn m a b = LayoutInfo -> a -> m b
+
+calculateLayout :: Ptr C'YGNode -> IO ()
+calculateLayout ptr =
+ let n = (nan :: CFloat)
+ in c'YGNodeStyleGetDirection ptr >>= c'YGNodeCalculateLayout ptr n n
+
+layoutInfo :: Ptr C'YGNode -> IO LayoutInfo
+layoutInfo ptr = do
+ left <- realToFrac <$> c'YGNodeLayoutGetLeft ptr
+ top <- realToFrac <$> c'YGNodeLayoutGetTop ptr
+ width <- realToFrac <$> c'YGNodeLayoutGetWidth ptr
+ 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
+ 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
+
+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)
+
+-- | 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
+-- node. The second result is stored as the new payload for the given node.
+-- Hence, the resulting monadic action produces a 'mappend'-ed set of 'b's and
+-- 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
diff --git a/yoga.cabal b/yoga.cabal
new file mode 100644
index 0000000..8022406
--- /dev/null
+++ b/yoga.cabal
@@ -0,0 +1,85 @@
+-- Initial yoga.cabal generated by cabal init. For further documentation,
+-- see http://haskell.org/cabal/users-guide/
+
+name: yoga
+version: 0.0.0.1
+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
+ written in C that implements flexbox. There are two
+ main ways to interface with these bindings. The high-level
+ bindings are stored in the Yoga module and provide a more
+ Haskell-like interface to the library. The other option is to
+ directly use the C-level bindings in "Bindings.Yoga". If you do
+ so you do so at your own risk (i.e. you must manage your own
+ memory).
+
+ The examples are not built nor included by default. Please
+ refer to the source tarball for examples on how to use this
+ library.
+
+ These bindings are not affiliated with Facebook in any way,
+ and have been developed separately for the sole purpose of
+ interfacing with their open source library.
+license: BSD3
+license-file: LICENSE
+author: Pavel Krajcevski
+maintainer: Krajcevski@gmail.com
+copyright: 2017-present
+category: Graphics
+build-type: Simple
+cabal-version: >=1.10
+
+source-repository head
+ type: git
+ location: https://www.github.com/Mokosha/yoga-hs.git
+
+library
+ default-language: Haskell2010
+ hs-source-dirs: lib
+
+ ghc-options: -Wall
+ if impl(ghc >= 8.0)
+ ghc-options: -Wno-redundant-constraints
+ if impl(ghc >= 6.8)
+ ghc-options: -fwarn-tabs
+ build-tools: hsc2hs
+ include-dirs: yoga/
+ install-includes: YGEnums.h
+ YGMacros.h
+ YGMemFuncs.h
+ YGNodeList.h
+ Yoga.h
+ c-sources: yoga/YGNodeList.c
+ yoga/Yoga.c
+
+ cc-options: -Wno-format -std=gnu99
+
+ exposed-modules:
+ Bindings.Yoga
+ Yoga
+
+ other-modules:
+ Bindings.Yoga.Enums
+
+ build-depends:
+ base >= 4 && < 6,
+ ieee754 >= 0.7,
+ bindings-DSL == 1.0.*
+
+flag examples
+ description: Build examples
+ default: False
+
+executable yoga-example
+ default-language: Haskell2010
+ main-is: Main.hs
+ hs-source-dirs: examples
+ ghc-options: -Wall -rtsopts -O3
+ build-depends: base > 4,
+ yoga
+
+ if flag(examples)
+ buildable: True
+ else
+ buildable: False
diff --git a/yoga/YGEnums.h b/yoga/YGEnums.h
new file mode 100644
index 0000000..6022f55
--- /dev/null
+++ b/yoga/YGEnums.h
@@ -0,0 +1,135 @@
+/**
+ * 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
diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h
new file mode 100644
index 0000000..2d8f728
--- /dev/null
+++ b/yoga/YGMacros.h
@@ -0,0 +1,61 @@
+/**
+ * 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
diff --git a/yoga/YGMemFuncs.h b/yoga/YGMemFuncs.h
new file mode 100644
index 0000000..b19751b
--- /dev/null
+++ b/yoga/YGMemFuncs.h
@@ -0,0 +1,16 @@
+#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
new file mode 100644
index 0000000..d82753e
--- /dev/null
+++ b/yoga/YGNodeList.c
@@ -0,0 +1,104 @@
+/**
+ * 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
new file mode 100644
index 0000000..41e272a
--- /dev/null
+++ b/yoga/YGNodeList.h
@@ -0,0 +1,33 @@
+/**
+ * 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/Yoga.c b/yoga/Yoga.c
new file mode 100644
index 0000000..52999d2
--- /dev/null
+++ b/yoga/Yoga.c
@@ -0,0 +1,3487 @@
+/**
+ * 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;
+ }
+}
diff --git a/yoga/Yoga.h b/yoga/Yoga.h
new file mode 100644
index 0000000..d5c5258
--- /dev/null
+++ b/yoga/Yoga.h
@@ -0,0 +1,247 @@
+/**
+ * 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