Repository: fyne-io/fyne
File: layout/rowwrap.go (Go)

Code before:
func NewRowWrapLayout() fyne.Layout {
	return &rowWrapLayout{
		horizontalPadding: theme.Padding(),
		verticalPadding:   theme.Padding(),
	}
}

// NewRowWrapLayoutWithCustomPadding creates a new RowWrapLayout instance
// with the specified paddings.
//
// Since: 2.7
func NewRowWrapLayoutWithCustomPadding(horizontal, vertical float32) fyne.Layout {
	return &rowWrapLayout{
		horizontalPadding: horizontal,
		verticalPadding:   vertical,
	}
}

var _ fyne.Layout = (*rowWrapLayout)(nil)

// MinSize finds the smallest size that satisfies all the child objects.
// For a RowWrapLayout this is the width of the widest child
// and the height of the first child multiplied by the number of children,
// with appropriate padding between them.
func (l *rowWrapLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
	if len(objects) == 0 {
		return fyne.NewSize(0, 0)
	}
	rows := l.rowCount
	if rows == 0 {
		rows = 1
	}
	rowHeight := objects[0].MinSize().Height
	var width float32
	for _, o := range objects {
		size := o.MinSize()
		if size.Width > width {
			width = size.Width
		}
	}
	s := fyne.NewSize(width, rowHeight*float32(rows)+l.verticalPadding*float32(rows-1))
	return s
}

// Layout is called to pack all child objects into a specified size.
// For RowWrapLayout this will arrange all objects into rows of equal size.
func (l *rowWrapLayout) Layout(objects []fyne.CanvasObject, containerSize fyne.Size) {
	if len(objects) == 0 {
		return
	}
	rowHeight := objects[0].MinSize().Height

Review comment (suggestion):
For something with wrapping like this, (and Label/RichText etc with wrapping), the behavior needed IIRC is for the MinSize returned to depend on the current size of the object. If the object hasn't been Resized (IE its size is 0, 0), then it returns the MinSize as you currently calculate it. But if it has been resized to a width >= the min width, the MinSize height is based on how many rows it needs to lay things out *at the current width*. Otherwise, placing it as the child of another layout will always make it take up more height than needed.

Not sure how to do this with a layout though, given that they are stateless by design. I suppose in Layout you could remeber the size in the `rowWrapLayout` struct, and then reference it in MinSize

Code after:
	p := theme.Padding()
	return &rowWrapLayout{
		horizontalPadding: p,
		verticalPadding:   p,
	}
}

// NewRowWrapLayoutWithCustomPadding returns a new RowWrapLayout instance
// with custom horizontal and inner padding.
//
// Since: 2.7
func NewRowWrapLayoutWithCustomPadding(horizontal, vertical float32) fyne.Layout {
	return &rowWrapLayout{
		horizontalPadding: horizontal,
		verticalPadding:   vertical,
	}
}

var _ fyne.Layout = (*rowWrapLayout)(nil)

// MinSize finds the smallest size that satisfies all the child objects.
// For a RowWrapLayout this is initially the width of the widest child
// and the height of the tallest child multiplied by the number of children,
// with appropriate padding between them.
// After Layout() has run it returns the actual min size.
func (l *rowWrapLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
	if len(objects) == 0 {
		return fyne.NewSize(0, 0)
	}
	if !l.minSize.IsZero() {
		return l.minSize
	}
	var maxW, maxH float32
	var objCount int
	for _, o := range objects {
		if !o.Visible() {
			continue
		}
		objCount++
		s := o.MinSize()
		maxW = fyne.Max(maxW, s.Width)
		maxH = fyne.Max(maxH, s.Height)
	}
	return fyne.NewSize(maxW, l.minHeight(maxH, objCount))
}

func (l *rowWrapLayout) minHeight(rowHeight float32, rowCount int) float32 {
	return rowHeight*float32(rowCount) + l.verticalPadding*float32(rowCount-1)
}

// Layout is called to pack all child objects into a specified size.