Simplified Grid's code greatly, making it more correct in the process. Also renamed h/vexpand/align to x/yexpand/align for simplicity.

This commit is contained in:
Pietro Gagliardi 2014-09-01 00:36:20 -04:00
parent c91cbf12b8
commit 2731cf3ae0
1 changed files with 79 additions and 123 deletions

200
grid.go
View File

@ -17,7 +17,7 @@ type Grid interface {
// Otherwise, it is placed relative to nextTo. // Otherwise, it is placed relative to nextTo.
// If nextTo is nil, it is placed next to the previously added Control, // If nextTo is nil, it is placed next to the previously added Control,
// The effect of adding the same Control multiple times is undefined, as is the effect of adding a Control next to one not present in the Grid. // The effect of adding the same Control multiple times is undefined, as is the effect of adding a Control next to one not present in the Grid.
Add(control Control, nextTo Control, side Side, hexpand bool, halign Align, vexpand bool, valign Align) Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align)
} }
// Align represents the alignment of a Control in its cell of a Grid. // Align represents the alignment of a Control in its cell of a Grid.
@ -49,14 +49,14 @@ type grid struct {
// for allocate() and preferredSize() // for allocate() and preferredSize()
xoff, yoff int xoff, yoff int
xmax, ymax int xmax, ymax int
grid [][]gridCellAllocation grid [][]Control
} }
type gridCell struct { type gridCell struct {
hexpand bool xexpand bool
halign Align xalign Align
vexpand bool yexpand bool
valign Align yalign Align
neighbors [nSides]Control neighbors [nSides]Control
// for allocate() and preferredSize() // for allocate() and preferredSize()
@ -74,12 +74,12 @@ func NewGrid() Grid {
} }
} }
func (g *grid) Add(control Control, nextTo Control, side Side, hexpand bool, halign Align, vexpand bool, valign Align) { func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align) {
cell := &gridCell{ cell := &gridCell{
hexpand: hexpand, xexpand: xexpand,
halign: halign, xalign: xalign,
vexpand: vexpand, yexpand: yexpand,
valign: valign, yalign: yalign,
} }
// if this is the first control, just add it in directly // if this is the first control, just add it in directly
if len(g.controls) != 0 { if len(g.controls) != 0 {
@ -135,12 +135,6 @@ func (g *grid) trasverse(c Control, x int, y int) {
} }
} }
type gridCellAllocation struct {
width int
height int
c Control
}
func (g *grid) buildGrid() { func (g *grid) buildGrid() {
// thanks to http://programmers.stackexchange.com/a/254968/147812 // thanks to http://programmers.stackexchange.com/a/254968/147812
// before we do anything, reset the visited bits // before we do anything, reset the visited bits
@ -170,10 +164,10 @@ func (g *grid) buildGrid() {
g.xmax++ g.xmax++
g.ymax++ g.ymax++
// and finally build the matrix // and finally build the matrix
g.grid = make([][]gridCellAllocation, g.ymax) g.grid = make([][]Control, g.ymax)
for y := 0; y < g.ymax; y++ { for y := 0; y < g.ymax; y++ {
g.grid[y] = make([]gridCellAllocation, g.xmax) g.grid[y] = make([]Control, g.xmax)
// the field c is assigned below for efficiency // the elements are assigned below for efficiency
} }
} }
@ -188,88 +182,60 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
width -= d.xpadding * g.xmax width -= d.xpadding * g.xmax
height -= d.ypadding * g.ymax height -= d.ypadding * g.ymax
// 2) for every control that doesn't expand, set the width of each cell of its column/height of each cell of its row to the largest such // 2) for every control, set the width of each cell of its column/height of each cell of its row to the largest such
nhexpand := make([]bool, g.xmax) colwidths := make([]int, g.xmax)
nvexpand := make([]bool, g.ymax) rowheights := make([]int, g.ymax)
colxexpand := make([]bool, g.xmax)
rowyexpand := make([]bool, g.ymax)
for c, cell := range g.controls { for c, cell := range g.controls {
width, height := c.preferredSize(d) width, height := c.preferredSize(d)
cell.width = width cell.width = width
cell.height = height cell.height = height
if !cell.hexpand { if colwidths[cell.gridx] < width {
g.grid[cell.gridy][cell.gridx].width = width colwidths[cell.gridx] = width
} else {
nhexpand[cell.gridx] = true
} }
if !cell.vexpand { if rowheights[cell.gridy] < height {
g.grid[cell.gridy][cell.gridx].height = height rowheights[cell.gridy] = height
} else {
nvexpand[cell.gridy] = true
} }
g.grid[cell.gridy][cell.gridx].c = c if cell.xexpand {
colxexpand[cell.gridx] = true
} }
// cells on the same row have the same height if cell.yexpand {
for y := 0; y < g.ymax; y++ { rowyexpand[cell.gridy] = true
max := 0
for x := 0; x < g.xmax; x++ {
if max < g.grid[y][x].height {
max = g.grid[y][x].height
}
}
for x := 0; x < g.xmax; x++ {
g.grid[y][x].height = max
}
}
// cells on the same column have the same width
for x := 0; x < g.xmax; x++ {
max := 0
for y := 0; y < g.ymax; y++ {
if max < g.grid[y][x].width {
max = g.grid[y][x].width
}
}
for y := 0; y < g.ymax; y++ {
g.grid[y][x].width = max
} }
g.grid[cell.gridy][cell.gridx] = c
} }
// 3) distribute the remaining space equally to expanding cells, adjusting widths and heights as needed // 3) distribute the remaining space equally to expanding cells, adjusting widths and heights as needed
nh := 0 nexpand := 0
for x, b := range nhexpand { for x, expand := range colxexpand {
if b { if expand {
nh++ nexpand++
} else { // column width known; subtract it } else { // column width known; subtract it
width -= g.grid[0][x].width width -= colwidths[x]
} }
} }
if nh > 0 { if nexpand > 0 {
h := width / nh w := width / nexpand
for y, b := range nhexpand { for x, expand := range colxexpand {
if b { if expand {
for x := 0; x < g.xmax; x++ { colwidths[x] = w
if g.grid[y][x].width < h {
g.grid[y][x].width = h
} }
} }
} }
nexpand = 0
for y, expand := range rowyexpand {
if expand {
nexpand++
} else { // row height known; subtract it
height -= rowheights[y]
} }
} }
nv := 0 if nexpand > 0 {
for y, b := range nvexpand { h := height / nexpand
if b { for y, expand := range rowyexpand {
nv++ if expand {
} else { // column height known; subtract it rowheights[y] = h
height -= g.grid[y][0].height
}
}
if nv > 0 {
v := height / nv
for x, b := range nvexpand {
if b {
for y := 0; y < g.ymax; y++ {
if g.grid[y][x].height < v {
g.grid[y][x].height = v
}
}
} }
} }
} }
@ -278,30 +244,30 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
// 4) handle alignment // 4) handle alignment
for _, cell := range g.controls { for _, cell := range g.controls {
if cell.hexpand { if cell.xexpand {
switch cell.halign { switch cell.xalign {
case LeftTop: case LeftTop:
// do nothing; this is the default // do nothing; this is the default
case Center: case Center:
case RightBottom: case RightBottom:
// TODO // TODO
case Fill: case Fill:
cell.width = g.grid[cell.gridy][cell.gridx].width cell.width = colwidths[cell.gridx]
default: default:
panic(fmt.Errorf("invalid halign %d in Grid.allocate()", cell.halign)) panic(fmt.Errorf("invalid xalign %d in Grid.allocate()", cell.xalign))
} }
} }
if cell.vexpand { if cell.yexpand {
switch cell.valign { switch cell.yalign {
case LeftTop: case LeftTop:
// do nothing; this is the default // do nothing; this is the default
case Center: case Center:
case RightBottom: case RightBottom:
// TODO // TODO
case Fill: case Fill:
cell.height = g.grid[cell.gridy][cell.gridx].height cell.height = rowheights[cell.gridy]
default: default:
panic(fmt.Errorf("invalid valign %d in Grid.allocate()", cell.valign)) panic(fmt.Errorf("invalid yalign %d in Grid.allocate()", cell.yalign))
} }
} }
} }
@ -312,11 +278,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
startx := x startx := x
for row, xcol := range g.grid { for row, xcol := range g.grid {
current = nil current = nil
for col, ca := range xcol { for col, c := range xcol {
cell := g.controls[ca.c] cell := g.controls[c]
as := ca.c.allocate(x, y, cell.width, cell.height, d) as := c.allocate(x, y, cell.width, cell.height, d)
if current != nil { // connect first left to first right if current != nil { // connect first left to first right
current.neighbor = ca.c current.neighbor = c
} }
if len(as) != 0 { if len(as) != 0 {
current = as[0] // next left is first subwidget current = as[0] // next left is first subwidget
@ -324,10 +290,10 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
current = nil // spaces don't have allocation data current = nil // spaces don't have allocation data
} }
allocations = append(allocations, as...) allocations = append(allocations, as...)
x += g.grid[0][col].width + d.xpadding x += colwidths[col] + d.xpadding
} }
x = startx x = startx
y += g.grid[row][0].height + d.ypadding y += rowheights[row] + d.ypadding
} }
return allocations return allocations
@ -343,38 +309,28 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
g.buildGrid() g.buildGrid()
// 2) for every control (including those that don't expand), set the width of each cell of its column/height of each cell of its row to the largest such // 2) for every control (including those that don't expand), set the width of each cell of its column/height of each cell of its row to the largest such
colwidths := make([]int, g.xmax)
rowheights := make([]int, g.ymax)
for c, cell := range g.controls { for c, cell := range g.controls {
width, height := c.preferredSize(d) width, height := c.preferredSize(d)
g.grid[cell.gridy][cell.gridx].width = width cell.width = width
g.grid[cell.gridy][cell.gridx].height = height cell.height = height
if colwidths[cell.gridx] < width {
colwidths[cell.gridx] = width
} }
// cells on the same row have the same height if rowheights[cell.gridy] < height {
maxy := 0 rowheights[cell.gridy] = height
for y := 0; y < g.ymax; y++ {
max := 0
for x := 0; x < g.xmax; x++ {
if max < g.grid[y][x].height {
max = g.grid[y][x].height
} }
} }
for x := 0; x < g.xmax; x++ {
g.grid[y][x].height = max // 3) and sum the widths and heights
}
maxy += max
}
// cells on the same column have the same width
maxx := 0 maxx := 0
for x := 0; x < g.xmax; x++ { for _, x := range colwidths {
max := 0 maxx += x
for y := 0; y < g.ymax; y++ {
if max < g.grid[y][x].width {
max = g.grid[y][x].width
} }
} maxy := 0
for y := 0; y < g.ymax; y++ { for _, y := range rowheights {
g.grid[y][x].width = max maxy += y
}
maxx += max
} }
// and that's it really; just discount the padding // and that's it really; just discount the padding