// A Grid arranges Controls in a two-dimensional grid.
// The height of each row and the width of each column is the maximum preferred height and width (respectively) of all the controls in that row or column (respectively).
// Controls are aligned to the top left corner of each cell.
// All Controls in a Grid maintain their preferred sizes by default; if a Control is marked as being "filling", it will be sized to fill its cell.
// Even if a Control is marked as filling, its preferred size is used to calculate cell sizes.
// One Control can be marked as "stretchy": when the Window containing the Grid is resized, the cell containing that Control resizes to take any remaining space; its row and column are adjusted accordingly (so other filling controls in the same row and column will fill to the new height and width, respectively).
// A stretchy Control implicitly fills its cell.
// All cooridnates in a Grid are given in (row,column) form with (0,0) being the top-left cell.
typeGridstruct{
createdbool
controls[][]Control
filling[][]bool
stretchyrow,stretchycolint
widths,heights[][]int// caches to avoid reallocating each time
rowheights,colwidths[]int
}
// NewGrid creates a new Grid with the given Controls.
// NewGrid needs to know the number of Controls in a row (alternatively, the number of columns); it will determine the number in a column from the number of Controls given.
// NewGrid panics if not given a full grid of Controls.
// Example:
// grid := NewGrid(3,
// control00, control01, control02,
// control10, control11, control12,
// control20, control21, control22)
funcNewGrid(nPerRowint,controls...Control)*Grid{
iflen(controls)%nPerRow!=0{
panic(fmt.Errorf("incomplete grid given to NewGrid() (not enough controls to evenly divide %d controls into rows of %d controls each)",len(controls),nPerRow))
}
nRows:=len(controls)/nPerRow
cc:=make([][]Control,nRows)
cf:=make([][]bool,nRows)
cw:=make([][]int,nRows)
ch:=make([][]int,nRows)
i:=0
forrow:=0;row<nRows;row++{
cc[row]=make([]Control,nPerRow)
cf[row]=make([]bool,nPerRow)
cw[row]=make([]int,nPerRow)
ch[row]=make([]int,nPerRow)
forx:=0;x<nPerRow;x++{
cc[row][x]=controls[i]
i++
}
}
return&Grid{
controls:cc,
filling:cf,
stretchyrow:-1,
stretchycol:-1,
widths:cw,
heights:ch,
rowheights:make([]int,nRows),
colwidths:make([]int,nPerRow),
}
}
// SetFilling marks the given Control of the Grid as filling its cell instead of staying at its preferred size.
// This function cannot be called after the Window that contains the Grid has been created.
// It panics if the given coordinate is invalid.
func(g*Grid)SetFilling(rowint,columnint){
ifg.created{
panic(fmt.Errorf("Grid.SetFilling() called after window create"))
panic(fmt.Errorf("internal inconsistency in Grid: stretchy (%d,%d) impossible (one component, not both, is -1/no stretchy control) in Grid.make()",g.stretchyrow,g.stretchycol))
}
forrow,xcol:=rangeg.controls{
forcol,c:=rangexcol{
err:=c.make(window)
iferr!=nil{
returnfmt.Errorf("error adding control (%d,%d) to Grid: %v",row,col,err)