From cc6b38d600ace6cfedac3463545e7777b509032e Mon Sep 17 00:00:00 2001 From: gizak Date: Tue, 3 Feb 2015 20:56:49 -0500 Subject: [PATCH] Add gauge and list component --- div.go => block.go | 27 +++++++---------- box.go | 74 ++++++++++++++++++++++------------------------ gauge.go | 56 +++++++++++++++++++++++++++++++++++ helper.go | 25 ++++++++++++++++ list.go | 71 ++++++++++++++++++++++++++++++++++++++++++++ p.go | 22 ++++++-------- point.go | 4 +-- render.go | 12 ++++---- 8 files changed, 216 insertions(+), 75 deletions(-) rename div.go => block.go (66%) create mode 100644 gauge.go create mode 100644 list.go diff --git a/div.go b/block.go similarity index 66% rename from div.go rename to block.go index 7859c8a..a45e825 100644 --- a/div.go +++ b/block.go @@ -1,14 +1,12 @@ package termui -import tm "github.com/nsf/termbox-go" - -type Div struct { +type Block struct { X int Y int - Border LabeledBox + Border labeledBorder IsDisplay bool HasBorder bool - BgColor tm.Attribute + BgColor Attribute Width int Height int innerWidth int @@ -17,21 +15,16 @@ type Div struct { innerY int } -func NewDiv() Div { - d := Div{} - d.Border.BgColor = tm.ColorDefault - d.Border.FgColor = tm.ColorDefault - d.Border.LabelFgColor = tm.ColorDefault - d.Border.LabelBgColor = tm.ColorDefault +func NewBlock() *Block { + d := Block{} d.IsDisplay = true d.HasBorder = true d.Width = 2 d.Height = 2 - d.BgColor = tm.ColorDefault - return d + return &d } -func (d *Div) sync() { +func (d *Block) align() { d.innerWidth = d.Width d.innerHeight = d.Height d.innerX = d.X @@ -49,8 +42,8 @@ func (d *Div) sync() { } } -func (d Div) Buffer() []Point { - (&d).sync() +func (d *Block) Buffer() []Point { + d.align() ps := []Point{} if !d.IsDisplay { @@ -67,7 +60,7 @@ func (d Div) Buffer() []Point { p.X = d.X + 1 + i p.Y = d.Y + 1 + j p.Code.Ch = ' ' - p.Code.Bg = d.BgColor + p.Code.Bg = toTmAttr(d.BgColor) ps = append(ps, p) } } diff --git a/box.go b/box.go index 48ebc1a..ffa8206 100644 --- a/box.go +++ b/box.go @@ -1,7 +1,5 @@ package termui -import tm "github.com/nsf/termbox-go" - const TOP_RIGHT = '┐' const VERTICAL_LINE = '│' const HORIZONTAL_LINE = '─' @@ -9,56 +7,56 @@ const TOP_LEFT = '┌' const BOTTOM_RIGHT = '┘' const BOTTOM_LEFT = '└' -type Box struct { +type border struct { X int Y int Width int Height int - FgColor tm.Attribute - BgColor tm.Attribute + FgColor Attribute + BgColor Attribute } -type HLine struct { +type hline struct { X int Y int Length int - FgColor tm.Attribute - BgColor tm.Attribute + FgColor Attribute + BgColor Attribute } -type VLine struct { +type vline struct { X int Y int Length int - FgColor tm.Attribute - BgColor tm.Attribute + FgColor Attribute + BgColor Attribute } -func (l HLine) Buffer() []Point { +func (l hline) Buffer() []Point { pts := make([]Point, l.Length) for i := 0; i < l.Length; i++ { pts[i].X = l.X + i pts[i].Y = l.Y pts[i].Code.Ch = HORIZONTAL_LINE - pts[i].Code.Bg = l.BgColor - pts[i].Code.Fg = l.FgColor + pts[i].Code.Bg = toTmAttr(l.BgColor) + pts[i].Code.Fg = toTmAttr(l.FgColor) } return pts } -func (l VLine) Buffer() []Point { +func (l vline) Buffer() []Point { pts := make([]Point, l.Length) for i := 0; i < l.Length; i++ { pts[i].X = l.X pts[i].Y = l.Y + i pts[i].Code.Ch = VERTICAL_LINE - pts[i].Code.Bg = l.BgColor - pts[i].Code.Fg = l.FgColor + pts[i].Code.Bg = toTmAttr(l.BgColor) + pts[i].Code.Fg = toTmAttr(l.FgColor) } return pts } -func (b Box) Buffer() []Point { +func (b border) Buffer() []Point { if b.Width < 2 || b.Height < 2 { return nil } @@ -66,45 +64,45 @@ func (b Box) Buffer() []Point { pts[0].X = b.X pts[0].Y = b.Y - pts[0].Code.Fg = b.FgColor - pts[0].Code.Bg = b.BgColor + pts[0].Code.Fg = toTmAttr(b.FgColor) + pts[0].Code.Bg = toTmAttr(b.BgColor) pts[0].Code.Ch = TOP_LEFT pts[1].X = b.X + b.Width - 1 pts[1].Y = b.Y - pts[1].Code.Fg = b.FgColor - pts[1].Code.Bg = b.BgColor + pts[1].Code.Fg = toTmAttr(b.FgColor) + pts[1].Code.Bg = toTmAttr(b.BgColor) pts[1].Code.Ch = TOP_RIGHT pts[2].X = b.X pts[2].Y = b.Y + b.Height - 1 - pts[2].Code.Fg = b.FgColor - pts[2].Code.Bg = b.BgColor + pts[2].Code.Fg = toTmAttr(b.FgColor) + pts[2].Code.Bg = toTmAttr(b.BgColor) pts[2].Code.Ch = BOTTOM_LEFT pts[3].X = b.X + b.Width - 1 pts[3].Y = b.Y + b.Height - 1 - pts[3].Code.Fg = b.FgColor - pts[3].Code.Bg = b.BgColor + pts[3].Code.Fg = toTmAttr(b.FgColor) + pts[3].Code.Bg = toTmAttr(b.BgColor) pts[3].Code.Ch = BOTTOM_RIGHT - copy(pts[4:], (HLine{b.X + 1, b.Y, b.Width - 2, b.FgColor, b.BgColor}).Buffer()) - copy(pts[4+b.Width-2:], (HLine{b.X + 1, b.Y + b.Height - 1, b.Width - 2, b.FgColor, b.BgColor}).Buffer()) - copy(pts[4+2*b.Width-4:], (VLine{b.X, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer()) - copy(pts[4+2*b.Width-4+b.Height-2:], (VLine{b.X + b.Width - 1, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer()) + copy(pts[4:], (hline{b.X + 1, b.Y, b.Width - 2, b.FgColor, b.BgColor}).Buffer()) + copy(pts[4+b.Width-2:], (hline{b.X + 1, b.Y + b.Height - 1, b.Width - 2, b.FgColor, b.BgColor}).Buffer()) + copy(pts[4+2*b.Width-4:], (vline{b.X, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer()) + copy(pts[4+2*b.Width-4+b.Height-2:], (vline{b.X + b.Width - 1, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer()) return pts } -type LabeledBox struct { - Box +type labeledBorder struct { + border Label string - LabelFgColor tm.Attribute - LabelBgColor tm.Attribute + LabelFgColor Attribute + LabelBgColor Attribute } -func (lb LabeledBox) Buffer() []Point { - ps := lb.Box.Buffer() +func (lb labeledBorder) Buffer() []Point { + ps := lb.border.Buffer() maxTxtW := lb.Width - 2 rs := trimStr2Runes(lb.Label, maxTxtW) @@ -113,8 +111,8 @@ func (lb LabeledBox) Buffer() []Point { p.X = lb.X + 1 + i p.Y = lb.Y p.Code.Ch = rs[i] - p.Code.Fg = lb.LabelFgColor - p.Code.Bg = lb.LabelBgColor + p.Code.Fg = toTmAttr(lb.LabelFgColor) + p.Code.Bg = toTmAttr(lb.LabelBgColor) ps = append(ps, p) } diff --git a/gauge.go b/gauge.go new file mode 100644 index 0000000..abe68d1 --- /dev/null +++ b/gauge.go @@ -0,0 +1,56 @@ +package termui + +import "strconv" + +type Gauge struct { + Block + Percent int + BarColor Attribute + PercentColor Attribute +} + +func NewGauge() *Gauge { + g := &Gauge{Block: *NewBlock(), PercentColor: ColorWhite, BarColor: ColorGreen} + g.Width = 12 + g.Height = 5 + return g +} + +func (g *Gauge) Buffer() []Point { + ps := g.Block.Buffer() + + w := g.Percent * g.innerWidth / 100 + s := strconv.Itoa(g.Percent) + "%" + rs := str2runes(s) + + prx := g.innerX + g.innerWidth/2 - 1 + pry := g.innerY + g.innerHeight/2 + + // plot bar + for i := 0; i < g.innerHeight; i++ { + for j := 0; j < w; j++ { + p := Point{} + p.X = g.innerX + j + p.Y = g.innerY + i + p.Code.Ch = ' ' + p.Code.Bg = toTmAttr(g.BarColor) + ps = append(ps, p) + } + } + + // plot percentage + for i, v := range rs { + p := Point{} + p.X = prx + i + p.Y = pry + p.Code.Ch = v + p.Code.Fg = toTmAttr(g.PercentColor) + if w > g.innerWidth/2-1+i { + p.Code.Bg = toTmAttr(g.BarColor) + } else { + p.Code.Bg = toTmAttr(g.Block.BgColor) + } + ps = append(ps, p) + } + return ps +} diff --git a/helper.go b/helper.go index aa9e28b..5efb386 100644 --- a/helper.go +++ b/helper.go @@ -2,6 +2,31 @@ package termui import "unicode/utf8" import "strings" +import tm "github.com/nsf/termbox-go" + +type Attribute uint16 + +const ( + ColorDefault Attribute = iota + ColorBlack + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +const ( + AttrBold Attribute = 1 << (iota + 9) + AttrUnderline + AttrReverse +) + +func toTmAttr(x Attribute) tm.Attribute { + return tm.Attribute(x) +} func str2runes(s string) []rune { n := utf8.RuneCountInString(s) diff --git a/list.go b/list.go new file mode 100644 index 0000000..3d9ae98 --- /dev/null +++ b/list.go @@ -0,0 +1,71 @@ +package termui + +import "strings" + +type List struct { + Block + Items []string + Overflow string + ItemFgColor Attribute + ItemBgColor Attribute +} + +func NewList() *List { + l := &List{Block: *NewBlock()} + l.Overflow = "hidden" + return l +} + +func (l *List) Buffer() []Point { + ps := l.Block.Buffer() + switch l.Overflow { + case "wrap": + rs := str2runes(strings.Join(l.Items, "\n")) + i, j, k := 0, 0, 0 + for i < l.innerHeight && k < len(rs) { + if rs[k] == '\n' || j == l.innerWidth { + i++ + j = 0 + if rs[k] == '\n' { + k++ + } + continue + } + pi := Point{} + pi.X = l.innerX + j + pi.Y = l.innerY + i + + pi.Code.Ch = rs[k] + pi.Code.Bg = toTmAttr(l.ItemBgColor) + pi.Code.Fg = toTmAttr(l.ItemFgColor) + + ps = append(ps, pi) + k++ + j++ + } + + case "hidden": + trimItems := l.Items + if len(trimItems) > l.innerHeight { + trimItems = trimItems[:l.innerHeight] + } + for i, v := range trimItems { + rs := trimStr2Runes(v, l.innerWidth) + + j := 0 + for _, vv := range rs { + p := Point{} + p.X = l.innerX + j + p.Y = l.innerY + i + + p.Code.Ch = vv + p.Code.Bg = toTmAttr(l.ItemBgColor) + p.Code.Fg = toTmAttr(l.ItemFgColor) + + ps = append(ps, p) + j++ + } + } + } + return ps +} diff --git a/p.go b/p.go index 3ab4877..90f9d2b 100644 --- a/p.go +++ b/p.go @@ -1,22 +1,18 @@ package termui -import tm "github.com/nsf/termbox-go" - type P struct { - Div + Block Text string - TextFgColor tm.Attribute - TextBgColor tm.Attribute + TextFgColor Attribute + TextBgColor Attribute } -func NewP(s string) P { - return P{Div: NewDiv(), Text: s} +func NewP(s string) *P { + return &P{Block: *NewBlock(), Text: s} } -func (p P) Buffer() []Point { - ps := p.Div.Buffer() - - (&p).sync() +func (p *P) Buffer() []Point { + ps := p.Block.Buffer() rs := str2runes(p.Text) i, j, k := 0, 0, 0 @@ -34,8 +30,8 @@ func (p P) Buffer() []Point { pi.Y = p.innerY + i pi.Code.Ch = rs[k] - pi.Code.Bg = p.TextBgColor - pi.Code.Fg = p.TextFgColor + pi.Code.Bg = toTmAttr(p.TextBgColor) + pi.Code.Fg = toTmAttr(p.TextFgColor) ps = append(ps, pi) k++ diff --git a/point.go b/point.go index e3619b8..c1a095c 100644 --- a/point.go +++ b/point.go @@ -4,6 +4,6 @@ import tm "github.com/nsf/termbox-go" type Point struct { Code tm.Cell - X int - Y int + X int + Y int } diff --git a/render.go b/render.go index 1ca1d38..f3d4045 100644 --- a/render.go +++ b/render.go @@ -10,14 +10,16 @@ func Init() error { return tm.Init() } -func Close(){ +func Close() { tm.Close() } -func Render(r Renderer) { - buf := r.Buffer() - for _,v := range buf { - tm.SetCell(v.X,v.Y,v.Code.Ch,v.Code.Fg,v.Code.Bg) +func Render(rs ...Renderer) { + for _, r := range rs { + buf := r.Buffer() + for _, v := range buf { + tm.SetCell(v.X, v.Y, v.Code.Ch, v.Code.Fg, v.Code.Bg) + } } tm.Flush() }