Rewrite grid
This commit is contained in:
parent
8ed1b40acf
commit
0d3c4485b2
202
grid.go
202
grid.go
@ -2,9 +2,10 @@ package termui
|
||||
|
||||
import tm "github.com/nsf/termbox-go"
|
||||
|
||||
/*
|
||||
type container struct {
|
||||
//Height int
|
||||
//Width int
|
||||
height int
|
||||
width int
|
||||
BgColor Attribute
|
||||
Rows []Row
|
||||
}
|
||||
@ -12,16 +13,14 @@ type container struct {
|
||||
type Row []Col
|
||||
|
||||
type Col struct {
|
||||
ColumnBufferer
|
||||
Blocks []ColumnBufferer
|
||||
Offset int // 0 ~ 11
|
||||
Span int // 1 ~ 12
|
||||
Sticky bool
|
||||
}
|
||||
|
||||
type ColumnBufferer interface {
|
||||
Bufferer
|
||||
GetHeight() int
|
||||
GetWidth() int
|
||||
SetWidth(int)
|
||||
SetX(int)
|
||||
SetY(int)
|
||||
@ -31,8 +30,64 @@ func NewRow(cols ...Col) Row {
|
||||
return cols
|
||||
}
|
||||
|
||||
func NewCol(block ColumnBufferer, span, offset int, sticky bool) Col {
|
||||
return Col{ColumnBufferer: block, Span: span, Sticky: sticky, Offset: offset}
|
||||
func NewCol(span, offset int, blocks ...ColumnBufferer) Col {
|
||||
return Col{Blocks: blocks, Span: span, Offset: offset}
|
||||
}
|
||||
|
||||
// Highest col is the height of a Row
|
||||
func (r Row) GetHeight() int {
|
||||
h := 0
|
||||
for _, v := range r {
|
||||
if nh := v.GetHeight(); nh > h {
|
||||
h = nh
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// Set width according to its span
|
||||
func (r Row) SetWidth(w int) {
|
||||
for _, c := range r {
|
||||
c.SetWidth(int(float64(w*c.Span) / 12.0))
|
||||
}
|
||||
}
|
||||
|
||||
// Set x y
|
||||
func (r Row) SetX(x int) {
|
||||
for i := range r {
|
||||
r[i].SetX(x)
|
||||
}
|
||||
}
|
||||
|
||||
func (r Row) SetY(y int) {
|
||||
for i := range r {
|
||||
r[i].SetY(y)
|
||||
}
|
||||
}
|
||||
|
||||
// GetHeight recursively retrieves height of each children, then add them up.
|
||||
func (c Col) GetHeight() int {
|
||||
h := 0
|
||||
for _, v := range c.Blocks {
|
||||
h += c.GetHeight()
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (c Col) GetWidth() int {
|
||||
w := 0
|
||||
for _, v := range c.Blocks {
|
||||
if nw := v.GetWidth(); nw > w {
|
||||
w = nw
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (c Col) SetWidth(w int) {
|
||||
for i := range c.Blocks {
|
||||
c.SetWidth(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (c container) Buffer() []Point {
|
||||
@ -50,7 +105,7 @@ func (c container) Buffer() []Point {
|
||||
}
|
||||
|
||||
w := int(float64(maxw*(col.Span+col.Offset)) / 12.0)
|
||||
if col.Sticky || col.GetWidth() > w {
|
||||
if col.GetWidth() > w {
|
||||
col.SetWidth(w)
|
||||
}
|
||||
|
||||
@ -63,5 +118,134 @@ func (c container) Buffer() []Point {
|
||||
}
|
||||
return ps
|
||||
}
|
||||
*/
|
||||
|
||||
var Body container
|
||||
type LayoutBufferer interface {
|
||||
Bufferer
|
||||
GetHeight() int
|
||||
SetWidth(int)
|
||||
SetX(int)
|
||||
SetY(int)
|
||||
}
|
||||
|
||||
// build a layout tree
|
||||
type row struct {
|
||||
Cols []*row
|
||||
Widget LayoutBufferer // only leaves hold this
|
||||
X int
|
||||
Y int
|
||||
Width int
|
||||
Height int
|
||||
Span int
|
||||
Offset int
|
||||
}
|
||||
|
||||
func newContainer() *row {
|
||||
w, _ := tm.Size()
|
||||
r := &row{Width: w, Span: 12, X: 0, Y: 0, Cols: []*row{}}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *row) layout() {
|
||||
r.assignWidth(r.Width)
|
||||
r.solveHeight()
|
||||
r.assignX(r.X)
|
||||
r.assignY(r.Y)
|
||||
}
|
||||
|
||||
func (r *row) isLeaf() bool {
|
||||
return r.Cols == nil || len(r.Cols) == 0
|
||||
}
|
||||
|
||||
func (r *row) isRenderableLeaf() bool {
|
||||
return r.isLeaf() && r.Widget != nil
|
||||
}
|
||||
|
||||
func (r *row) assignWidth(w int) {
|
||||
cw := int(float64(w*r.Span) / 12)
|
||||
r.Width = cw
|
||||
|
||||
for i, _ := range r.Cols {
|
||||
r.Cols[i].assignWidth(cw)
|
||||
}
|
||||
}
|
||||
|
||||
// bottom up
|
||||
func (r *row) solveHeight() int {
|
||||
if r.isRenderableLeaf() {
|
||||
r.Height = r.Widget.GetHeight()
|
||||
return r.Widget.GetHeight()
|
||||
}
|
||||
|
||||
maxh := 0
|
||||
if !r.isLeaf() {
|
||||
for _, c := range r.Cols {
|
||||
nh := c.solveHeight()
|
||||
// when embed rows in Cols, row widgets stack up
|
||||
if r.Widget != nil {
|
||||
nh += r.Widget.GetHeight()
|
||||
}
|
||||
if nh > maxh {
|
||||
maxh = nh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Height = maxh
|
||||
return maxh
|
||||
}
|
||||
|
||||
func (r *row) assignX(x int) {
|
||||
if r.isRenderableLeaf() {
|
||||
r.Widget.SetX(x)
|
||||
}
|
||||
|
||||
if !r.isLeaf() {
|
||||
acc := 0
|
||||
for i, c := range r.Cols {
|
||||
r.Cols[i].assignX(x + acc)
|
||||
acc += c.Width
|
||||
if c.Offset != 0 {
|
||||
acc += int(float64(c.Offset*c.Width) / float64(12*c.Span))
|
||||
}
|
||||
}
|
||||
}
|
||||
r.X = x
|
||||
}
|
||||
|
||||
func (r *row) assignY(y int) {
|
||||
r.Y = y
|
||||
|
||||
if r.isRenderableLeaf() {
|
||||
r.Widget.SetY(y)
|
||||
return
|
||||
}
|
||||
|
||||
for i := range r.Cols {
|
||||
acc := 0
|
||||
if r.Widget != nil {
|
||||
acc = r.Widget.GetHeight()
|
||||
}
|
||||
r.Cols[i].assignY(y + acc)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// recursively merge all widgets buffer
|
||||
func (r *row) Buffer() []Point {
|
||||
merged := []Point{}
|
||||
|
||||
if r.isRenderableLeaf() {
|
||||
return r.Widget.Buffer()
|
||||
}
|
||||
|
||||
if !r.isLeaf() {
|
||||
for _, c := range r.Cols {
|
||||
merged = append(merged, c.Buffer()...)
|
||||
}
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
|
||||
//var Body container
|
||||
|
83
grid_test.go
Normal file
83
grid_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
package termui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
var r *row
|
||||
|
||||
func TestRowWidth(t *testing.T) {
|
||||
p0 := NewPar("p0")
|
||||
p0.Height = 1
|
||||
p1 := NewPar("p1")
|
||||
p1.Height = 1
|
||||
p2 := NewPar("p2")
|
||||
p2.Height = 1
|
||||
p3 := NewPar("p3")
|
||||
p3.Height = 1
|
||||
|
||||
/* test against tree:
|
||||
|
||||
r
|
||||
/ \
|
||||
0:w 1
|
||||
/ \
|
||||
10:w 11
|
||||
/
|
||||
110:w
|
||||
/
|
||||
1100:w
|
||||
*/
|
||||
r = &row{
|
||||
Span: 12,
|
||||
Cols: []*row{
|
||||
&row{Widget: p0, Span: 6},
|
||||
&row{
|
||||
Span: 6,
|
||||
Cols: []*row{
|
||||
&row{Widget: p1, Span: 6},
|
||||
&row{
|
||||
Span: 6,
|
||||
Cols: []*row{
|
||||
&row{
|
||||
Span: 12,
|
||||
Widget: p2,
|
||||
Cols: []*row{
|
||||
&row{Span: 12, Widget: p3}}}}}}}}}
|
||||
r.assignWidth(100)
|
||||
if r.Width != 100 ||
|
||||
(r.Cols[0].Width) != 50 ||
|
||||
(r.Cols[1].Width) != 50 ||
|
||||
(r.Cols[1].Cols[0].Width) != 25 ||
|
||||
(r.Cols[1].Cols[1].Width) != 25 ||
|
||||
(r.Cols[1].Cols[1].Cols[0].Width) != 25 ||
|
||||
(r.Cols[1].Cols[1].Cols[0].Cols[0].Width) != 25 {
|
||||
t.Error("assignWidth fails")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRowHeight(t *testing.T) {
|
||||
spew.Dump()
|
||||
if (r.solveHeight()) != 2 ||
|
||||
(r.Cols[1].Cols[1].Height) != 2 ||
|
||||
(r.Cols[1].Cols[1].Cols[0].Height) != 2 ||
|
||||
(r.Cols[1].Cols[0].Height) != 1 {
|
||||
t.Error("solveHeight fails")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssignXY(t *testing.T) {
|
||||
r.assignX(0)
|
||||
r.assignY(0)
|
||||
if (r.Cols[0].X) != 0 ||
|
||||
(r.Cols[1].Cols[0].X) != 50 ||
|
||||
(r.Cols[1].Cols[1].X) != 75 ||
|
||||
(r.Cols[1].Cols[1].Cols[0].X) != 75 ||
|
||||
(r.Cols[1].Cols[0].Y) != 0 ||
|
||||
(r.Cols[1].Cols[1].Cols[0].Y) != 0 ||
|
||||
(r.Cols[1].Cols[1].Cols[0].Cols[0].Y) != 1 {
|
||||
t.Error("assignXY fails")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user