It’s a pure Go
port of Facebook’s
Yoga C library.
High-level API overview
Despite implementing CSS flexbox spec, it isn’t tied to CSS/HTML in any way. Yoga, for example, can be integrated with iOS app and used to layout UIView hierarchy.
The library works on abstract tree of nodes. In HTML a node would correspond to a block element like a div
. When used in Cocoa app, a node could represent UIView
or NSView
.
When used on windows, it could represent a HWND-based control.
The high-level use is:
- create a tree of nodes that represents a layout you want to represent
- set desired flexbox properties on each node using
node.StyleSet*()
functions
- call
flex.CalculateLayout(rootNode, parentWidth, parentHeight, direction)
- each node is now measured and positioned so you can e.g. size and position widgets associated with each node. You can get the size on position of nodes with
node.LayoutGet*()
functions
- when layout hierachy changes (e.g. the node represents a label and you’ve changed its text, which changes it’s intrinsic size), call
node.MarkDirty()
and CalculateLayout()
to re-calculate new size/position of the nodes
An exmple
Let’s assume that we want to re-create the following HTML layout:
<div id="percentage_multiple_nested_with_padding_margin_and_percentage_values" style="width: 200px; height: 200px; flex-direction: column;">
<div style="flex-grow: 1; flex-basis: 10%; min-width: 60%; margin: 5px; padding: 3px;">
<div style="width: 50%; margin: 5px; padding: 3%;">
<div style="width: 45%; margin: 5%; padding: 3px;"></div>
</div>
</div>
<div style="flex-grow: 4; flex-basis: 15%; min-width: 20%;"></div>
</div>
The equivalent Go code is:
config := flex.NewConfig()
root := flex.NewNodeWithConfig(config)
root.StyleSetWidth(200)
root.StyleSetHeight(200)
rootChild0 := flex.NewNodeWithConfig(config)
rootChild0.StyleSetFlexGrow(1)
rootChild0.StyleSetFlexBasisPercent(10)
rootChild0.StyleSetMargin(EdgeLeft, 5)
rootChild0.StyleSetMargin(EdgeTop, 5)
rootChild0.StyleSetMargin(EdgeRight, 5)
rootChild0.StyleSetMargin(EdgeBottom, 5)
rootChild0.StyleSetPadding(EdgeLeft, 3)
rootChild0.StyleSetPadding(EdgeTop, 3)
rootChild0.StyleSetPadding(EdgeRight, 3)
rootChild0.StyleSetPadding(EdgeBottom, 3)
rootChild0.StyleSetMinWidthPercent(60)
root.InsertChild(rootChild0, 0)
rootChild0Child0 := flex.NewNodeWithConfig(config)
rootChild0Child0.StyleSetMargin(EdgeLeft, 5)
rootChild0Child0.StyleSetMargin(EdgeTop, 5)
rootChild0Child0.StyleSetMargin(EdgeRight, 5)
rootChild0Child0.StyleSetMargin(EdgeBottom, 5)
rootChild0Child0.StyleSetPaddingPercent(EdgeLeft, 3)
rootChild0Child0.StyleSetPaddingPercent(EdgeTop, 3)
rootChild0Child0.StyleSetPaddingPercent(EdgeRight, 3)
rootChild0Child0.StyleSetPaddingPercent(EdgeBottom, 3)
rootChild0Child0.StyleSetWidthPercent(50)
rootChild0.InsertChild(rootChild0Child0, 0)
rootChild0Child0Child0 := flex.NewNodeWithConfig(config)
rootChild0Child0Child0.StyleSetMarginPercent(EdgeLeft, 5)
rootChild0Child0Child0.StyleSetMarginPercent(EdgeTop, 5)
rootChild0Child0Child0.StyleSetMarginPercent(EdgeRight, 5)
rootChild0Child0Child0.StyleSetMarginPercent(EdgeBottom, 5)
rootChild0Child0Child0.StyleSetPadding(EdgeLeft, 3)
rootChild0Child0Child0.StyleSetPadding(EdgeTop, 3)
rootChild0Child0Child0.StyleSetPadding(EdgeRight, 3)
rootChild0Child0Child0.StyleSetPadding(EdgeBottom, 3)
rootChild0Child0Child0.StyleSetWidthPercent(45)
rootChild0Child0.InsertChild(rootChild0Child0Child0, 0)
rootChild1 := flex.NewNodeWithConfig(config)
rootChild1.StyleSetFlexGrow(4)
rootChild1.StyleSetFlexBasisPercent(15)
rootChild1.StyleSetMinWidthPercent(20)
root.InsertChild(rootChild1, 1)
flex.CalculateLayout(root, flex.Undefined, flex.Undefined, DirectionLTR)
After CalculateLayout
we can see the position of each node e.g.:
fmt.Printf("root left: %f\n", root.LayoutGetLeft()) // 0
fmt.Printf"root top: %f\n", root.LayoutGetTop()) // 0
fmt.Printf("root width: %f\n", root.LayoutGetWidth()) // 200
fmt.Printf("root height: %f\n", root.LayoutGetHeight()) // 200
Each file there has corresponding
*_test.go
file in
github.com/kjk/flex directory which shows how to express it in Go.
Size of root’s parent
Notice that in this particular example we used flex.Undefined
as both height and width of the parent container.
Imagine you’re using flex
to implment layout for a dekstop application where each flex.Node
represents a control inside the window.
Window is the parent of root node.
In response to user resizing the window, you want to pass width/height of the window to flex.CalculateLayout()
.
When you create the window initially, you might do the reverse: pass flex.Undefined
as width/height of parent container and then use the size of root node as the size of the window, to size it to its content.
Measure function
Imagine that a node represents an OS button. The button has some intrisic size dictated by its text.
To represent that size flex
allows setting a measuring function with node.SetMeasureFunc(measureFunc MeasureFunc)
. It’s definition is:
type MeasureFunc func(node *Node, width float32, widthMode MeasureMode, height float32, heightMode MeasureMode) Size
The functions takes a hint width/height which is the size of parent container and returns intrinsic size of node.
This is usefule e.g. when a node represents a paragraph of text. When you know width of the parent container, you can break it into multi-line text.
If measuring function needs some state, you can use node.Context
to store it.