Skip to content

Commit

Permalink
Rewrite grid
Browse files Browse the repository at this point in the history
  • Loading branch information
gizak committed Mar 19, 2015
1 parent 8ed1b40 commit 0d3c448
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 13 deletions.
202 changes: 193 additions & 9 deletions grid.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,25 @@ package termui

import tm "github.com/nsf/termbox-go"

/*
type container struct {
//Height int
//Width int
height int
width int
BgColor Attribute
Rows []Row
}
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)
Expand All @@ -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 {
Expand All @@ -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)
}
Expand All @@ -63,5 +118,134 @@ func (c container) Buffer() []Point {
}
return ps
}
*/

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
//var Body container
83 changes: 83 additions & 0 deletions grid_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
8 changes: 4 additions & 4 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down

0 comments on commit 0d3c448

Please sign in to comment.