153 lines
4.3 KiB
Go
153 lines
4.3 KiB
Go
|
package layout
|
||
|
|
||
|
import (
|
||
|
"fyne.io/fyne/v2"
|
||
|
"fyne.io/fyne/v2/canvas"
|
||
|
"fyne.io/fyne/v2/theme"
|
||
|
)
|
||
|
|
||
|
const formLayoutCols = 2
|
||
|
|
||
|
// Declare conformity with Layout interface
|
||
|
var _ fyne.Layout = (*formLayout)(nil)
|
||
|
|
||
|
// formLayout is two column grid where each row has a label and a widget.
|
||
|
type formLayout struct {
|
||
|
}
|
||
|
|
||
|
func (f *formLayout) countRows(objects []fyne.CanvasObject) int {
|
||
|
count := 0
|
||
|
|
||
|
for i := 0; i < len(objects); i += formLayoutCols {
|
||
|
if !objects[i].Visible() && !objects[i+1].Visible() {
|
||
|
continue
|
||
|
}
|
||
|
count++
|
||
|
}
|
||
|
|
||
|
return count
|
||
|
}
|
||
|
|
||
|
// tableCellsSize defines the size for all the cells of the form table.
|
||
|
// The height of each row will be set as the max value between the label and content cell heights.
|
||
|
// The width of the label column will be set as the max width value between all the label cells.
|
||
|
// The width of the content column will be set as the max width value between all the content cells
|
||
|
// or the remaining space of the bounding containerWidth, if it is larger.
|
||
|
func (f *formLayout) tableCellsSize(objects []fyne.CanvasObject, containerWidth float32) [][2]fyne.Size {
|
||
|
rows := f.countRows(objects)
|
||
|
table := make([][2]fyne.Size, rows)
|
||
|
|
||
|
if (len(objects))%formLayoutCols != 0 {
|
||
|
return table
|
||
|
}
|
||
|
|
||
|
padding := theme.Padding()
|
||
|
lowBound := 0
|
||
|
highBound := 2
|
||
|
labelCellMaxWidth := float32(0)
|
||
|
contentCellMaxWidth := float32(0)
|
||
|
for row := 0; row < rows; {
|
||
|
currentRow := objects[lowBound:highBound]
|
||
|
lowBound = highBound
|
||
|
highBound += formLayoutCols
|
||
|
if !currentRow[0].Visible() && !currentRow[1].Visible() {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
labelCell := currentRow[0].MinSize()
|
||
|
if _, ok := currentRow[0].(*canvas.Text); ok {
|
||
|
labelCell.Width += padding * 4
|
||
|
}
|
||
|
labelCellMaxWidth = fyne.Max(labelCellMaxWidth, labelCell.Width)
|
||
|
|
||
|
contentCell := currentRow[1].MinSize()
|
||
|
contentCellMaxWidth = fyne.Max(contentCellMaxWidth, contentCell.Width)
|
||
|
|
||
|
rowHeight := fyne.Max(labelCell.Height, contentCell.Height)
|
||
|
|
||
|
labelCell.Height = rowHeight
|
||
|
contentCell.Height = rowHeight
|
||
|
|
||
|
table[row][0] = labelCell
|
||
|
table[row][1] = contentCell
|
||
|
row++
|
||
|
}
|
||
|
|
||
|
contentWidth := fyne.Max(contentCellMaxWidth, containerWidth-labelCellMaxWidth-padding)
|
||
|
for row := 0; row < rows; row++ {
|
||
|
table[row][0].Width = labelCellMaxWidth
|
||
|
table[row][1].Width = contentWidth
|
||
|
}
|
||
|
|
||
|
return table
|
||
|
}
|
||
|
|
||
|
// Layout is called to pack all child objects into a table format with two columns.
|
||
|
func (f *formLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
|
||
|
table := f.tableCellsSize(objects, size.Width)
|
||
|
|
||
|
padding := theme.Padding()
|
||
|
innerPadding := theme.InnerPadding()
|
||
|
|
||
|
row := 0
|
||
|
y := float32(0)
|
||
|
for i := 0; i < len(objects); i += formLayoutCols {
|
||
|
if !objects[i].Visible() && (i+1 < len(objects) && !objects[i+1].Visible()) {
|
||
|
continue
|
||
|
}
|
||
|
if row > 0 {
|
||
|
y += table[row-1][0].Height + padding
|
||
|
}
|
||
|
|
||
|
tableRow := table[row]
|
||
|
if _, ok := objects[i].(*canvas.Text); ok {
|
||
|
objects[i].Move(fyne.NewPos(innerPadding, y+innerPadding))
|
||
|
objects[i].Resize(fyne.NewSize(tableRow[0].Width-innerPadding*2, objects[i].MinSize().Height))
|
||
|
} else {
|
||
|
objects[i].Move(fyne.NewPos(0, y))
|
||
|
objects[i].Resize(fyne.NewSize(tableRow[0].Width, tableRow[0].Height))
|
||
|
}
|
||
|
|
||
|
if i+1 < len(objects) {
|
||
|
if _, ok := objects[i+1].(*canvas.Text); ok {
|
||
|
objects[i+1].Move(fyne.NewPos(padding+tableRow[0].Width+innerPadding, y+innerPadding))
|
||
|
objects[i+1].Resize(fyne.NewSize(tableRow[1].Width-innerPadding*2, objects[i+1].MinSize().Height))
|
||
|
} else {
|
||
|
objects[i+1].Move(fyne.NewPos(padding+tableRow[0].Width, y))
|
||
|
objects[i+1].Resize(fyne.NewSize(tableRow[1].Width, tableRow[0].Height))
|
||
|
}
|
||
|
}
|
||
|
row++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MinSize finds the smallest size that satisfies all the child objects.
|
||
|
// For a FormLayout this is the width of the widest label and content items and the height is
|
||
|
// the sum of all column children combined with padding between each.
|
||
|
func (f *formLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
|
||
|
table := f.tableCellsSize(objects, 0)
|
||
|
|
||
|
padding := theme.Padding()
|
||
|
minSize := fyne.NewSize(0, 0)
|
||
|
|
||
|
if len(table) == 0 {
|
||
|
return minSize
|
||
|
}
|
||
|
|
||
|
added := false
|
||
|
minSize.Width = table[0][0].Width + table[0][1].Width + padding
|
||
|
for row := 0; row < len(table); row++ {
|
||
|
minSize.Height += table[row][0].Height
|
||
|
if added {
|
||
|
minSize.Height += padding
|
||
|
}
|
||
|
added = true
|
||
|
}
|
||
|
return minSize
|
||
|
}
|
||
|
|
||
|
// NewFormLayout returns a new FormLayout instance
|
||
|
func NewFormLayout() fyne.Layout {
|
||
|
return &formLayout{}
|
||
|
}
|