#+TITLE: Yoga FFI Binding #+STARTUP: content #+FILETAGS: :cl-tui:yoga-ffi:v010: * Yoga FFI Binding CFFI bindings for Facebook's Yoga Flexbox layout engine. Provides raw access to the C library — node management, style setters, layout calculation, and layout getters. The next file (=layout-primitives.org=) wraps these in CLOS. The Yoga shared library is at =/usr/local/lib/libyoga.so=, built from =facebook/yoga= with C++17. ** Contract - =(load-yoga)= :: loads the shared library via CFFI. Signal an error if the library cannot be found. - =(yg-node-new)= → YGNodeRef :: wraps =YGNodeNew=. Allocates a new Yoga node. - =(yg-node-free node)= :: wraps =YGNodeFree=. Frees a node and detaches it from its owner and children. - =(yg-node-free-recursive node)= :: wraps =YGNodeFreeRecursive=. Frees the entire subtree. - =(yg-node-insert-child node child index)= :: wraps =YGNodeInsertChild=. Inserts a child at the given index. - =(yg-node-remove-child node child)= :: wraps =YGNodeRemoveChild=. - =(yg-node-get-child-count node)= → integer :: wraps =YGNodeGetChildCount=. - =(yg-node-calculate-layout node width height direction)= :: wraps =YGNodeCalculateLayout=. Runs the layout algorithm. - =(yg-node-layout-get-left node)= → float :: wraps =YGNodeLayoutGetLeft=. Returns the computed X position. - =(yg-node-layout-get-top node)= → float :: wraps =YGNodeLayoutGetTop=. Returns the computed Y position. - =(yg-node-layout-get-width node)= → float :: wraps =YGNodeLayoutGetWidth=. Returns the computed width. - =(yg-node-layout-get-height node)= → float :: wraps =YGNodeLayoutGetHeight=. Returns the computed height. - =(yg-node-style-set-direction node dir)= :: sets the text direction. - =(yg-node-style-set-flex-direction node dir)= :: sets the flex direction. - =(yg-node-style-set-justify-content node justify)= :: sets main-axis alignment. - =(yg-node-style-set-align-items node align)= :: sets cross-axis alignment. - =(yg-node-style-set-align-self node align)= :: sets self alignment. - =(yg-node-style-set-flex-wrap node wrap)= :: sets wrapping mode. - =(yg-node-style-set-flex-grow node value)= :: sets the flex grow factor. - =(yg-node-style-set-flex-shrink node value)= :: sets the flex shrink factor. - =(yg-node-style-set-position-type node type)= :: sets positioning type. - =(yg-node-style-set-width node points)= :: sets width in points. - =(yg-node-style-set-width-percent node pct)= :: sets width as percentage. - =(yg-node-style-set-width-auto node)= :: sets width to auto. - =(yg-node-style-set-height node points)= :: sets height in points. - =(yg-node-style-set-height-percent node pct)= :: sets height as percentage. - =(yg-node-style-set-height-auto node)= :: sets height to auto. - =(yg-node-style-set-padding node edge points)= :: sets padding on an edge. - =(yg-node-style-set-margin node edge points)= :: sets margin on an edge. - =(yg-node-style-set-border node edge points)= :: sets border on an edge. - =(yg-node-style-set-gap node gutter length)= :: sets gap between children. - =(yg-node-style-set-overflow node overflow)= :: sets overflow mode. - =(yg-node-style-set-display node display)= :: sets display mode. - =(yg-node-style-set-position-percent node edge pct)= :: sets position as percentage. - =(yg-node-style-set-position node edge points)= :: sets position in points. Enum keywords (mapping C enum → Lisp keyword): - YGDirection: :inherit (0), :ltr (1), :rtl (2) - YGFlexDirection: :column (0), :column-reverse (1), :row (2), :row-reverse (3) - YGJustify: :auto (0), :flex-start (1), :center (2), :flex-end (3), :space-between (4), :space-around (5), :space-evenly (6), :stretch (7) - YGAlign: :auto (0), :flex-start (1), :center (2), :flex-end (3), :stretch (4), :baseline (5), :space-between (6), :space-around (7), :space-evenly (8) - YGWrap: :nowrap (0), :wrap (1), :wrap-reverse (2) - YGPositionType: :static (0), :relative (1), :absolute (2) - YGOverflow: :visible (0), :hidden (1), :scroll (2) - YGDisplay: :flex (0), :none (1), :contents (2) - YGEdge: :left (0), :top (1), :right (2), :bottom (3), :start (4), :end (5), :horizontal (6), :vertical (7), :all (8) - YGGutter: :column (0), :row (1), :all (2) - YGUnit: :undefined (0), :point (1), :percent (2), :auto (3) * Application-Level Package #+begin_src lisp (eval-when (:compile-toplevel :load-toplevel :execute) (ql:quickload :cffi :silent t)) (defpackage :cl-tui.yoga-ffi (:use :cl) (:export ;; library #:*libyoga* #:load-yoga ;; constants #:+yg-direction-inherit+ #:+yg-direction-ltr+ #:+yg-direction-rtl+ #:+yg-flex-direction-column+ #:+yg-flex-direction-column-reverse+ #:+yg-flex-direction-row+ #:+yg-flex-direction-row-reverse+ #:+yg-justify-auto+ #:+yg-justify-flex-start+ #:+yg-justify-center+ #:+yg-justify-flex-end+ #:+yg-justify-space-between+ #:+yg-justify-space-around+ #:+yg-justify-space-evenly+ #:+yg-justify-stretch+ #:+yg-align-auto+ #:+yg-align-flex-start+ #:+yg-align-center+ #:+yg-align-flex-end+ #:+yg-align-stretch+ #:+yg-align-baseline+ #:+yg-align-space-between+ #:+yg-align-space-around+ #:+yg-align-space-evenly+ #:+yg-wrap-nowrap+ #:+yg-wrap-wrap+ #:+yg-wrap-wrap-reverse+ #:+yg-position-type-static+ #:+yg-position-type-relative+ #:+yg-position-type-absolute+ #:+yg-overflow-visible+ #:+yg-overflow-hidden+ #:+yg-overflow-scroll+ #:+yg-display-flex+ #:+yg-display-none+ #:+yg-display-contents+ #:+yg-edge-left+ #:+yg-edge-top+ #:+yg-edge-right+ #:+yg-edge-bottom+ #:+yg-edge-start+ #:+yg-edge-end+ #:+yg-edge-horizontal+ #:+yg-edge-vertical+ #:+yg-edge-all+ #:+yg-gutter-column+ #:+yg-gutter-row+ #:+yg-gutter-all+ #:+yg-unit-undefined+ #:+yg-unit-point+ #:+yg-unit-percent+ #:+yg-unit-auto+ ;; types #:yg-node-ref #:yg-node-const-ref ;; node management #:yg-node-new #:yg-node-free #:yg-node-free-recursive #:yg-node-insert-child #:yg-node-remove-child #:yg-node-remove-all-children #:yg-node-get-child-count #:yg-node-get-child ;; layout #:yg-node-calculate-layout #:yg-node-layout-get-left #:yg-node-layout-get-top #:yg-node-layout-get-width #:yg-node-layout-get-height #:yg-node-layout-get-right #:yg-node-layout-get-bottom #:yg-node-is-dirty #:yg-node-mark-dirty ;; style direction & flex #:yg-node-style-set-direction #:yg-node-style-set-flex-direction #:yg-node-style-set-justify-content #:yg-node-style-set-align-items #:yg-node-style-set-align-self #:yg-node-style-set-align-content #:yg-node-style-set-flex-wrap #:yg-node-style-set-position-type #:yg-node-style-set-flex-grow #:yg-node-style-set-flex-shrink #:yg-node-style-set-flex-basis #:yg-node-style-set-flex-basis-auto #:yg-node-style-set-flex-basis-percent #:yg-node-style-set-overflow #:yg-node-style-set-display ;; style dimensions #:yg-node-style-set-width #:yg-node-style-set-width-percent #:yg-node-style-set-width-auto #:yg-node-style-set-height #:yg-node-style-set-height-percent #:yg-node-style-set-height-auto #:yg-node-style-set-min-width #:yg-node-style-set-min-width-percent #:yg-node-style-set-min-height #:yg-node-style-set-min-height-percent #:yg-node-style-set-max-width #:yg-node-style-set-max-height #:yg-node-style-set-aspect-ratio ;; style padding/margin/border/gap/position #:yg-node-style-set-padding #:yg-node-style-set-padding-percent #:yg-node-style-set-margin #:yg-node-style-set-margin-percent #:yg-node-style-set-margin-auto #:yg-node-style-set-border #:yg-node-style-set-gap #:yg-node-style-set-position #:yg-node-style-set-position-percent ;; style getters #:yg-node-style-get-width #:yg-node-style-get-height #:yg-node-style-get-flex-direction #:yg-node-style-get-align-items #:yg-node-style-get-justify-content)) (in-package :cl-tui.yoga-ffi) #+end_src * Foreign Library Loading #+begin_src lisp (defparameter *libyoga* nil "Handle for the loaded Yoga shared library.") (defun load-yoga () "Load the Yoga shared library via CFFI." (setf *libyoga* (cffi:load-foreign-library "/usr/local/lib/libyoga.so")) (sb-int:set-floating-point-modes :traps '()) *libyoga*) (load-yoga) #+end_src * Enum Constants #+begin_src lisp (eval-when (:compile-toplevel :load-toplevel :execute) ;; YGDirection (defconstant +yg-direction-inherit+ 0) (defconstant +yg-direction-ltr+ 1) (defconstant +yg-direction-rtl+ 2) ;; YGFlexDirection (defconstant +yg-flex-direction-column+ 0) (defconstant +yg-flex-direction-column-reverse+ 1) (defconstant +yg-flex-direction-row+ 2) (defconstant +yg-flex-direction-row-reverse+ 3) ;; YGJustify (defconstant +yg-justify-auto+ 0) (defconstant +yg-justify-flex-start+ 1) (defconstant +yg-justify-center+ 2) (defconstant +yg-justify-flex-end+ 3) (defconstant +yg-justify-space-between+ 4) (defconstant +yg-justify-space-around+ 5) (defconstant +yg-justify-space-evenly+ 6) (defconstant +yg-justify-stretch+ 7) ;; YGAlign (defconstant +yg-align-auto+ 0) (defconstant +yg-align-flex-start+ 1) (defconstant +yg-align-center+ 2) (defconstant +yg-align-flex-end+ 3) (defconstant +yg-align-stretch+ 4) (defconstant +yg-align-baseline+ 5) (defconstant +yg-align-space-between+ 6) (defconstant +yg-align-space-around+ 7) (defconstant +yg-align-space-evenly+ 8) ;; YGWrap (defconstant +yg-wrap-nowrap+ 0) (defconstant +yg-wrap-wrap+ 1) (defconstant +yg-wrap-wrap-reverse+ 2) ;; YGPositionType (defconstant +yg-position-type-static+ 0) (defconstant +yg-position-type-relative+ 1) (defconstant +yg-position-type-absolute+ 2) ;; YGOverflow (defconstant +yg-overflow-visible+ 0) (defconstant +yg-overflow-hidden+ 1) (defconstant +yg-overflow-scroll+ 2) ;; YGDisplay (defconstant +yg-display-flex+ 0) (defconstant +yg-display-none+ 1) (defconstant +yg-display-contents+ 2) ;; YGEdge (defconstant +yg-edge-left+ 0) (defconstant +yg-edge-top+ 1) (defconstant +yg-edge-right+ 2) (defconstant +yg-edge-bottom+ 3) (defconstant +yg-edge-start+ 4) (defconstant +yg-edge-end+ 5) (defconstant +yg-edge-horizontal+ 6) (defconstant +yg-edge-vertical+ 7) (defconstant +yg-edge-all+ 8) ;; YGGutter (defconstant +yg-gutter-column+ 0) (defconstant +yg-gutter-row+ 1) (defconstant +yg-gutter-all+ 2) ;; YGUnit (defconstant +yg-unit-undefined+ 0) (defconstant +yg-unit-point+ 1) (defconstant +yg-unit-percent+ 2) (defconstant +yg-unit-auto+ 3)) #+end_src * CFFI Foreign Type Definitions #+begin_src lisp (cffi:defctype yg-node-ref :pointer) (cffi:defctype yg-node-const-ref :pointer) (cffi:defcstruct yg-size (width :float) (height :float)) (cffi:defcstruct yg-value (value :float) (unit :int)) #+end_src * Node Management #+begin_src lisp (cffi:defcfun ("YGNodeNew" yg-node-new) yg-node-ref) (cffi:defcfun ("YGNodeFree" yg-node-free) :void (node yg-node-ref)) (cffi:defcfun ("YGNodeFreeRecursive" yg-node-free-recursive) :void (node yg-node-ref)) (cffi:defcfun ("YGNodeInsertChild" yg-node-insert-child) :void (node yg-node-ref) (child yg-node-ref) (index :unsigned-int)) (cffi:defcfun ("YGNodeRemoveChild" yg-node-remove-child) :void (node yg-node-ref) (child yg-node-ref)) (cffi:defcfun ("YGNodeRemoveAllChildren" yg-node-remove-all-children) :void (node yg-node-ref)) (cffi:defcfun ("YGNodeGetChildCount" yg-node-get-child-count) :unsigned-int (node yg-node-const-ref)) (cffi:defcfun ("YGNodeGetChild" yg-node-get-child) yg-node-ref (node yg-node-ref) (index :unsigned-int)) #+end_src * Layout Calculation #+begin_src lisp (cffi:defcfun ("YGNodeCalculateLayout" yg-node-calculate-layout) :void (node yg-node-ref) (available-width :float) (available-height :float) (owner-direction :int)) (cffi:defcfun ("YGNodeLayoutGetLeft" yg-node-layout-get-left) :float (node yg-node-const-ref)) (cffi:defcfun ("YGNodeLayoutGetTop" yg-node-layout-get-top) :float (node yg-node-const-ref)) (cffi:defcfun ("YGNodeLayoutGetWidth" yg-node-layout-get-width) :float (node yg-node-const-ref)) (cffi:defcfun ("YGNodeLayoutGetHeight" yg-node-layout-get-height) :float (node yg-node-const-ref)) (cffi:defcfun ("YGNodeLayoutGetRight" yg-node-layout-get-right) :float (node yg-node-const-ref)) (cffi:defcfun ("YGNodeLayoutGetBottom" yg-node-layout-get-bottom) :float (node yg-node-const-ref)) (cffi:defcfun ("YGNodeIsDirty" yg-node-is-dirty) :boolean (node yg-node-const-ref)) (cffi:defcfun ("YGNodeMarkDirty" yg-node-mark-dirty) :void (node yg-node-ref)) #+end_src * Style Setters — Direction and Flex #+begin_src lisp (cffi:defcfun ("YGNodeStyleSetDirection" yg-node-style-set-direction) :void (node yg-node-ref) (direction :int)) (cffi:defcfun ("YGNodeStyleSetFlexDirection" yg-node-style-set-flex-direction) :void (node yg-node-ref) (flex-direction :int)) (cffi:defcfun ("YGNodeStyleSetJustifyContent" yg-node-style-set-justify-content) :void (node yg-node-ref) (justify :int)) (cffi:defcfun ("YGNodeStyleSetAlignItems" yg-node-style-set-align-items) :void (node yg-node-ref) (align :int)) (cffi:defcfun ("YGNodeStyleSetAlignSelf" yg-node-style-set-align-self) :void (node yg-node-ref) (align :int)) (cffi:defcfun ("YGNodeStyleSetAlignContent" yg-node-style-set-align-content) :void (node yg-node-ref) (align :int)) (cffi:defcfun ("YGNodeStyleSetFlexWrap" yg-node-style-set-flex-wrap) :void (node yg-node-ref) (wrap :int)) (cffi:defcfun ("YGNodeStyleSetPositionType" yg-node-style-set-position-type) :void (node yg-node-ref) (position-type :int)) (cffi:defcfun ("YGNodeStyleSetFlexGrow" yg-node-style-set-flex-grow) :void (node yg-node-ref) (flex-grow :float)) (cffi:defcfun ("YGNodeStyleSetFlexShrink" yg-node-style-set-flex-shrink) :void (node yg-node-ref) (flex-shrink :float)) (cffi:defcfun ("YGNodeStyleSetFlexBasis" yg-node-style-set-flex-basis) :void (node yg-node-ref) (flex-basis :float)) (cffi:defcfun ("YGNodeStyleSetFlexBasisAuto" yg-node-style-set-flex-basis-auto) :void (node yg-node-ref)) (cffi:defcfun ("YGNodeStyleSetFlexBasisPercent" yg-node-style-set-flex-basis-percent) :void (node yg-node-ref) (flex-basis :float)) (cffi:defcfun ("YGNodeStyleSetOverflow" yg-node-style-set-overflow) :void (node yg-node-ref) (overflow :int)) (cffi:defcfun ("YGNodeStyleSetDisplay" yg-node-style-set-display) :void (node yg-node-ref) (display :int)) #+end_src * Style Setters — Dimensions #+begin_src lisp (cffi:defcfun ("YGNodeStyleSetWidth" yg-node-style-set-width) :void (node yg-node-ref) (width :float)) (cffi:defcfun ("YGNodeStyleSetWidthPercent" yg-node-style-set-width-percent) :void (node yg-node-ref) (width :float)) (cffi:defcfun ("YGNodeStyleSetWidthAuto" yg-node-style-set-width-auto) :void (node yg-node-ref)) (cffi:defcfun ("YGNodeStyleSetHeight" yg-node-style-set-height) :void (node yg-node-ref) (height :float)) (cffi:defcfun ("YGNodeStyleSetHeightPercent" yg-node-style-set-height-percent) :void (node yg-node-ref) (height :float)) (cffi:defcfun ("YGNodeStyleSetHeightAuto" yg-node-style-set-height-auto) :void (node yg-node-ref)) (cffi:defcfun ("YGNodeStyleSetMinWidth" yg-node-style-set-min-width) :void (node yg-node-ref) (min-width :float)) (cffi:defcfun ("YGNodeStyleSetMinWidthPercent" yg-node-style-set-min-width-percent) :void (node yg-node-ref) (min-width :float)) (cffi:defcfun ("YGNodeStyleSetMinHeight" yg-node-style-set-min-height) :void (node yg-node-ref) (min-height :float)) (cffi:defcfun ("YGNodeStyleSetMinHeightPercent" yg-node-style-set-min-height-percent) :void (node yg-node-ref) (min-height :float)) (cffi:defcfun ("YGNodeStyleSetMaxWidth" yg-node-style-set-max-width) :void (node yg-node-ref) (max-width :float)) (cffi:defcfun ("YGNodeStyleSetMaxHeight" yg-node-style-set-max-height) :void (node yg-node-ref) (max-height :float)) (cffi:defcfun ("YGNodeStyleSetAspectRatio" yg-node-style-set-aspect-ratio) :void (node yg-node-ref) (aspect-ratio :float)) #+end_src * Style Setters — Padding, Margin, Border, Gap, Position #+begin_src lisp (cffi:defcfun ("YGNodeStyleSetPadding" yg-node-style-set-padding) :void (node yg-node-ref) (edge :int) (padding :float)) (cffi:defcfun ("YGNodeStyleSetPaddingPercent" yg-node-style-set-padding-percent) :void (node yg-node-ref) (edge :int) (padding :float)) (cffi:defcfun ("YGNodeStyleSetMargin" yg-node-style-set-margin) :void (node yg-node-ref) (edge :int) (margin :float)) (cffi:defcfun ("YGNodeStyleSetMarginPercent" yg-node-style-set-margin-percent) :void (node yg-node-ref) (edge :int) (margin :float)) (cffi:defcfun ("YGNodeStyleSetMarginAuto" yg-node-style-set-margin-auto) :void (node yg-node-ref) (edge :int)) (cffi:defcfun ("YGNodeStyleSetBorder" yg-node-style-set-border) :void (node yg-node-ref) (edge :int) (border :float)) (cffi:defcfun ("YGNodeStyleSetGap" yg-node-style-set-gap) :void (node yg-node-ref) (gutter :int) (gap :float)) (cffi:defcfun ("YGNodeStyleSetPosition" yg-node-style-set-position) :void (node yg-node-ref) (edge :int) (position :float)) (cffi:defcfun ("YGNodeStyleSetPositionPercent" yg-node-style-set-position-percent) :void (node yg-node-ref) (edge :int) (position :float)) #+end_src * Style Getters #+begin_src lisp (cffi:defcfun ("YGNodeStyleGetWidth" yg-node-style-get-width) yg-value (node yg-node-const-ref)) (cffi:defcfun ("YGNodeStyleGetHeight" yg-node-style-get-height) yg-value (node yg-node-const-ref)) (cffi:defcfun ("YGNodeStyleGetFlexDirection" yg-node-style-get-flex-direction) :int (node yg-node-const-ref)) (cffi:defcfun ("YGNodeStyleGetAlignItems" yg-node-style-get-align-items) :int (node yg-node-const-ref)) (cffi:defcfun ("YGNodeStyleGetJustifyContent" yg-node-style-get-justify-content) :int (node yg-node-const-ref)) #+end_src * Test Suite #+begin_src lisp (eval-when (:compile-toplevel :load-toplevel :execute) (ql:quickload :fiveam :silent t)) (defpackage :cl-tui.yoga-ffi-tests (:use :cl :fiveam) (:import-from :cl-tui.yoga-ffi #:load-yoga #:yg-node-new #:yg-node-free #:yg-node-free-recursive #:yg-node-insert-child #:yg-node-get-child-count #:yg-node-calculate-layout #:yg-node-layout-get-left #:yg-node-layout-get-top #:yg-node-layout-get-width #:yg-node-layout-get-height #:yg-node-style-set-flex-direction #:yg-node-style-set-justify-content #:yg-node-style-set-align-items #:yg-node-style-set-width #:yg-node-style-set-height #:yg-node-style-set-padding #:yg-node-style-set-margin #:yg-node-style-set-flex-grow #:yg-node-style-set-position-type #:yg-node-style-set-position #:+yg-flex-direction-column+ #:+yg-flex-direction-row+ #:+yg-justify-center+ #:+yg-justify-flex-start+ #:+yg-align-stretch+ #:+yg-align-center+ #:+yg-edge-all+ #:+yg-edge-left+ #:+yg-edge-top+ #:+yg-edge-right+ #:+yg-edge-bottom+ #:+yg-position-type-relative+ #:+yg-position-type-absolute+ ;; YGDirection constants (different from YGFlexDirection!) #:+yg-direction-inherit+ #:+yg-direction-ltr+ #:+yg-direction-rtl+)) (in-package :cl-tui.yoga-ffi-tests) (fiveam:def-suite yoga-ffi-suite :description "Yoga FFI binding verification") (fiveam:in-suite yoga-ffi-suite) (fiveam:test test-node-create-free "Contract: yg-node-new returns a non-null pointer; yg-node-free doesn't crash." (let ((node (yg-node-new))) (fiveam:is (not (cffi:null-pointer-p node))) (yg-node-free node) (fiveam:pass))) (fiveam:test test-node-child-count "Contract: yg-node-get-child-count returns 0 for a new node." (let ((node (yg-node-new))) (fiveam:is (= 0 (yg-node-get-child-count node))) (yg-node-free node))) (fiveam:test test-node-insert-child "Contract: inserting a child increments the child count." (let ((parent (yg-node-new)) (child (yg-node-new))) (yg-node-insert-child parent child 0) (fiveam:is (= 1 (yg-node-get-child-count parent))) (yg-node-free-recursive parent))) (fiveam:test test-layout-basic-column "Contract: a column with two fixed-height children positions them vertically." (let* ((root (yg-node-new))) (yg-node-style-set-width root 100.0) (yg-node-style-set-height root 200.0) (yg-node-style-set-flex-direction root +yg-flex-direction-column+) (let ((child1 (yg-node-new)) (child2 (yg-node-new))) (yg-node-style-set-width child1 100.0) (yg-node-style-set-height child1 50.0) (yg-node-style-set-width child2 100.0) (yg-node-style-set-height child2 50.0) (yg-node-insert-child root child1 0) (yg-node-insert-child root child2 1) (yg-node-calculate-layout root 100.0 200.0 +yg-direction-ltr+) (fiveam:is (= 0.0 (yg-node-layout-get-left child1))) (fiveam:is (= 0.0 (yg-node-layout-get-top child1))) (fiveam:is (= 50.0 (yg-node-layout-get-top child2))) (yg-node-free-recursive root)))) (fiveam:test test-layout-basic-row "Contract: a row with two fixed-width children positions them horizontally." (let* ((root (yg-node-new))) (yg-node-style-set-width root 200.0) (yg-node-style-set-height root 100.0) (yg-node-style-set-flex-direction root +yg-flex-direction-row+) (let ((child1 (yg-node-new)) (child2 (yg-node-new))) (yg-node-style-set-width child1 80.0) (yg-node-style-set-height child1 50.0) (yg-node-style-set-width child2 80.0) (yg-node-style-set-height child2 50.0) (yg-node-insert-child root child1 0) (yg-node-insert-child root child2 1) (yg-node-calculate-layout root 200.0 100.0 +yg-direction-ltr+) (fiveam:is (= 0.0 (yg-node-layout-get-left child1))) (fiveam:is (= 0.0 (yg-node-layout-get-top child1))) (fiveam:is (= 80.0 (yg-node-layout-get-left child2))) (yg-node-free-recursive root)))) (fiveam:test test-layout-flex-grow "Contract: flex-grow distributes remaining space proportionally." (let* ((root (yg-node-new))) (yg-node-style-set-width root 200.0) (yg-node-style-set-height root 100.0) (yg-node-style-set-flex-direction root +yg-flex-direction-row+) (let ((child1 (yg-node-new)) (child2 (yg-node-new))) (yg-node-style-set-height child1 100.0) (yg-node-style-set-flex-grow child1 1.0) (yg-node-style-set-height child2 100.0) (yg-node-style-set-flex-grow child2 2.0) (yg-node-insert-child root child1 0) (yg-node-insert-child root child2 1) (yg-node-calculate-layout root 200.0 100.0 +yg-direction-ltr+) (fiveam:is (< 0.0 (yg-node-layout-get-width child1))) (fiveam:is (< 0.0 (yg-node-layout-get-width child2))) (fiveam:is (= 200.0 (+ (yg-node-layout-get-width child1) (yg-node-layout-get-width child2)))) (yg-node-free-recursive root)))) (fiveam:test test-layout-absolute-position "Contract: an absolute child positions relative to its parent." (let* ((root (yg-node-new))) (yg-node-style-set-width root 300.0) (yg-node-style-set-height root 300.0) (let ((child (yg-node-new))) (yg-node-style-set-width child 50.0) (yg-node-style-set-height child 50.0) (yg-node-style-set-position-type child +yg-position-type-absolute+) (yg-node-style-set-position child +yg-edge-left+ 100.0) (yg-node-style-set-position child +yg-edge-top+ 50.0) (yg-node-insert-child root child 0) (yg-node-calculate-layout root 300.0 300.0 +yg-direction-ltr+) (fiveam:is (= 100.0 (yg-node-layout-get-left child))) (fiveam:is (= 50.0 (yg-node-layout-get-top child))) (fiveam:is (= 50.0 (yg-node-layout-get-width child))) (fiveam:is (= 50.0 (yg-node-layout-get-height child))) (yg-node-free-recursive root)))) (fiveam:test test-layout-padding "Contract: padding reduces the available space for children." (let* ((root (yg-node-new))) (yg-node-style-set-width root 200.0) (yg-node-style-set-height root 100.0) (yg-node-style-set-padding root +yg-edge-all+ 10.0) (let ((child (yg-node-new))) (yg-node-style-set-width child 180.0) (yg-node-style-set-height child 80.0) (yg-node-insert-child root child 0) (yg-node-calculate-layout root 200.0 100.0 +yg-direction-ltr+) (fiveam:is (= 10.0 (yg-node-layout-get-left child))) (fiveam:is (= 10.0 (yg-node-layout-get-top child))) (yg-node-free-recursive root)))) (fiveam:test test-layout-nested "Contract: nested containers produce correct leaf positions." (let* ((root (yg-node-new))) (yg-node-style-set-width root 400.0) (yg-node-style-set-height root 400.0) (yg-node-style-set-flex-direction root +yg-flex-direction-column+) (let ((outer (yg-node-new))) (yg-node-style-set-width outer 400.0) (yg-node-style-set-height outer 200.0) (yg-node-style-set-flex-direction outer +yg-flex-direction-row+) (let ((inner (yg-node-new))) (yg-node-style-set-width inner 100.0) (yg-node-style-set-height inner 100.0) (yg-node-insert-child outer inner 0) (yg-node-insert-child root outer 0) (yg-node-calculate-layout root 400.0 400.0 +yg-direction-ltr+) (fiveam:is (= 0.0 (yg-node-layout-get-left inner))) (fiveam:is (= 100.0 (yg-node-layout-get-width inner))) (fiveam:is (= 100.0 (yg-node-layout-get-height inner))) (yg-node-free-recursive root))))) #+end_src