diff --git a/grid.go b/grid.go index 5102211..8108906 100644 --- a/grid.go +++ b/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 diff --git a/grid_test.go b/grid_test.go new file mode 100644 index 0000000..cd906bb --- /dev/null +++ b/grid_test.go @@ -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") + } +} diff --git a/render.go b/render.go index ef2d128..c701f0b 100644 --- a/render.go +++ b/render.go @@ -8,10 +8,10 @@ type Bufferer interface { } func Init() error { - Body = container{ - BgColor: theme.BodyBg, - Rows: []Row{}, - } + // Body = container{ + // BgColor: theme.BodyBg, + // Rows: []Row{}, + // } return tm.Init() }