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:
parent
c91cbf12b8
commit
2731cf3ae0
200
grid.go
200
grid.go
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue