layout/flex.go
Functions
func Compute
func (f *FlexLayout) Compute(tree LayoutTree, root NodeID, available geometry.Size) Result {
style := tree.Style(root)
childCount := tree.ChildCount(root)
if childCount == 0 {
return Result{Size: geometry.Size{}}
}
isHorizontal := style.FlexDirection.IsHorizontal()
isReversed := style.FlexDirection.IsReversed()
// Get main/cross axis constraints
mainMax, crossMax := f.getAxisSizes(available, isHorizontal)
// Collect children and measure them
items := make([]flexItem, childCount)
for i := 0; i < childCount; i++ {
childID := tree.ChildAt(root, i)
childStyle := tree.Style(childID)
// Measure child with loose constraints
constraints := f.createMeasureConstraints(crossMax, isHorizontal)
childSize := tree.Measure(childID, constraints)
items[i] = flexItem{
id: childID,
grow: childStyle.FlexGrow,
shrink: childStyle.FlexShrink,
basis: childStyle.FlexBasis.Resolve(mainMax, 0),
mainSize: f.mainSize(childSize, isHorizontal),
crossSize: f.crossSize(childSize, isHorizontal),
}
// Use basis if specified, otherwise use measured size
if !childStyle.FlexBasis.IsAuto() {
items[i].mainSize = items[i].basis
}
}
// Calculate totals
totalMain := f.calculateTotalMain(items, style.Gap)
totalGrow := f.calculateTotalGrow(items)
totalShrink := f.calculateTotalShrink(items)
maxCross := f.calculateMaxCross(items)
// Distribute space
freeSpace := mainMax - totalMain
if freeSpace > 0 && totalGrow > 0 {
f.growItems(items, freeSpace, totalGrow)
} else if freeSpace < 0 && totalShrink > 0 {
f.shrinkItems(items, -freeSpace, totalShrink)
}
// Calculate positioning
numGaps := childCount - 1
if numGaps < 0 {
numGaps = 0
}
contentMain := f.sumMainSizes(items) + style.Gap*float32(numGaps)
mainStart, mainSpacing := f.calculateJustification(style, mainMax-contentMain, childCount)
// Position children
currentMain := mainStart
if isReversed {
currentMain = mainMax - mainStart
}
for i := range items {
item := &items[i]
// Calculate main position
var mainPos float32
if isReversed {
mainPos = currentMain - item.mainSize
currentMain -= item.mainSize + mainSpacing
} else {
mainPos = currentMain
currentMain += item.mainSize + mainSpacing
}
// Calculate cross position
crossPos := f.calculateCrossPosition(item, maxCross, style.AlignItems)
// Set layout
var pos geometry.Point
var size geometry.Size
if isHorizontal {
pos = geometry.Point{X: mainPos, Y: crossPos}
size = geometry.Size{Width: item.mainSize, Height: item.crossSize}
} else {
pos = geometry.Point{X: crossPos, Y: mainPos}
size = geometry.Size{Width: item.crossSize, Height: item.mainSize}
}
tree.SetLayout(item.id, NodeLayout{Position: pos, Size: size})
}
// Calculate final size
var resultSize geometry.Size
if isHorizontal {
resultSize = geometry.Size{Width: contentMain, Height: maxCross}
} else {
resultSize = geometry.Size{Width: maxCross, Height: contentMain}
}
return Result{
Size: resultSize,
Overflow: contentMain > mainMax,
}
}
func Name
Name returns "flex".
func (f *FlexLayout) Name() string {
return flexLayoutName
}
Structs
type FlexLayout struct
FlexLayout implements CSS Flexbox-style layout.
FlexLayout arranges children along a main axis (row or column),
distributing space according to flex properties from the node styles.
# Properties Used
From parent style:
- FlexDirection: main axis direction
- FlexWrap: whether items wrap
- JustifyContent: main axis space distribution
- AlignItems: cross axis alignment
- Gap: space between items
From child styles:
- FlexGrow: how much item grows to fill space
- FlexShrink: how much item shrinks when space is tight
- FlexBasis: initial size before grow/shrink
type FlexLayout struct{}
Compute performs flexbox layout on the tree starting at root.