From 7aed750f643db42ccd83fda7278b99b8fa734d6a Mon Sep 17 00:00:00 2001 From: gizak Date: Tue, 21 Apr 2015 09:56:10 -0400 Subject: [PATCH] WIP use Buffer instead of []Point in Bufferer Merge https://github.com/Matt3o12/termui.git colored-list Merge https://github.com/funkygao/termui.git master Add subdir widget Use image Rectangle represent buffer area --- block.go | 124 +++++------ box.go | 198 +++++++++--------- buffer.go | 89 ++++++++ example/gauge.go | 26 ++- gauge.go | 83 -------- grid.go | 17 +- helper.go | 57 +++++ point.go | 28 --- render.go | 17 +- textRender.go | 2 + bar.go => widget/barchart.go | 2 + canvas.go => widget/canvas.go | 2 + canvas_test.go => widget/canvas_test.go | 2 + widget/gauge.go | 67 ++++++ chart.go => widget/linechart.go | 2 + chart_others.go => widget/linechart_others.go | 2 + .../linechart_windows.go | 2 + list.go => widget/list.go | 2 + p.go => widget/par.go | 2 + sparkline.go => widget/sparkline.go | 2 + 20 files changed, 428 insertions(+), 298 deletions(-) create mode 100644 buffer.go delete mode 100644 gauge.go delete mode 100644 point.go rename bar.go => widget/barchart.go (99%) rename canvas.go => widget/canvas.go (98%) rename canvas_test.go => widget/canvas_test.go (97%) create mode 100644 widget/gauge.go rename chart.go => widget/linechart.go (99%) rename chart_others.go => widget/linechart_others.go (93%) rename chart_windows.go => widget/linechart_windows.go (93%) rename list.go => widget/list.go (99%) rename p.go => widget/par.go (98%) rename sparkline.go => widget/sparkline.go (99%) diff --git a/block.go b/block.go index 9531365..14b7819 100644 --- a/block.go +++ b/block.go @@ -4,22 +4,22 @@ package termui +import "image" + // Block is a base struct for all other upper level widgets, // consider it as css: display:block. // Normally you do not need to create it manually. type Block struct { + Area image.Rectangle + innerArea image.Rectangle X int Y int - Border labeledBorder + Border LabeledBorder IsDisplay bool HasBorder bool - BgColor Attribute + Bg Attribute Width int Height int - innerWidth int - innerHeight int - innerX int - innerY int PaddingTop int PaddingBottom int PaddingLeft int @@ -31,75 +31,81 @@ func NewBlock() *Block { d := Block{} d.IsDisplay = true d.HasBorder = theme.HasBorder - d.Border.BgColor = theme.BorderBg - d.Border.FgColor = theme.BorderFg - d.Border.LabelBgColor = theme.BorderLabelTextBg - d.Border.LabelFgColor = theme.BorderLabelTextFg - d.BgColor = theme.BlockBg + d.Border.Left = true + d.Border.Right = true + d.Border.Top = true + d.Border.Bottom = true + d.Border.Bg = theme.BorderBg + d.Border.Fg = theme.BorderFg + d.Border.LabelBgClr = theme.BorderLabelTextBg + d.Border.LabelFgClr = theme.BorderLabelTextFg + d.Bg = theme.BlockBg d.Width = 2 d.Height = 2 return &d } -// compute box model -func (d *Block) align() { - d.innerWidth = d.Width - d.PaddingLeft - d.PaddingRight - d.innerHeight = d.Height - d.PaddingTop - d.PaddingBottom - d.innerX = d.X + d.PaddingLeft - d.innerY = d.Y + d.PaddingTop +// Align computes box model +func (d *Block) Align() { + d.Area.Min.X = d.X + d.Area.Min.Y = d.Y + d.Area.Max.X = d.X + d.Width - 1 + d.Area.Max.Y = d.Y + d.Height - 1 + + d.innerArea.Min.X = d.X + d.PaddingLeft + d.innerArea.Min.Y = d.Y + d.PaddingTop + d.innerArea.Max.X = d.Area.Max.X - d.PaddingRight + d.innerArea.Max.Y = d.Area.Max.Y - d.PaddingBottom + + d.Border.Area = d.Area if d.HasBorder { - d.innerHeight -= 2 - d.innerWidth -= 2 - d.Border.X = d.X - d.Border.Y = d.Y - d.Border.Width = d.Width - d.Border.Height = d.Height - d.innerX++ - d.innerY++ + switch { + case d.Border.Left: + d.innerArea.Min.X++ + fallthrough + case d.Border.Right: + d.innerArea.Max.X-- + fallthrough + case d.Border.Top: + d.innerArea.Min.Y++ + fallthrough + case d.Border.Bottom: + d.innerArea.Max.Y-- + } } - - if d.innerHeight < 0 { - d.innerHeight = 0 - } - if d.innerWidth < 0 { - d.innerWidth = 0 - } - } // InnerBounds returns the internal bounds of the block after aligning and // calculating the padding and border, if any. -func (d *Block) InnerBounds() (x, y, width, height int) { - d.align() - return d.innerX, d.innerY, d.innerWidth, d.innerHeight +func (d *Block) InnerBounds() image.Rectangle { + d.Align() + return d.innerArea } // Buffer implements Bufferer interface. // Draw background and border (if any). -func (d *Block) Buffer() []Point { - d.align() +func (d *Block) Buffer() Buffer { + d.Align() - ps := []Point{} + buf := NewBuffer() + buf.Area = d.Area if !d.IsDisplay { - return ps + return buf } + // render border if d.HasBorder { - ps = d.Border.Buffer() + buf.Union(d.Border.Buffer()) } - for i := 0; i < d.innerWidth; i++ { - for j := 0; j < d.innerHeight; j++ { - p := Point{} - p.X = d.X + 1 + i - p.Y = d.Y + 1 + j - p.Ch = ' ' - p.Bg = d.BgColor - ps = append(ps, p) + // render background + for p := range buf.CellMap { + if p.In(d.innerArea) { + buf.CellMap[p] = Cell{' ', ColorDefault, d.Bg} } } - return ps + return buf } // GetHeight implements GridBufferer. @@ -122,21 +128,3 @@ func (d *Block) SetY(y int) { func (d *Block) SetWidth(w int) { d.Width = w } - -// chop the overflow parts -func (d *Block) chopOverflow(ps []Point) []Point { - nps := make([]Point, 0, len(ps)) - x := d.X - y := d.Y - w := d.Width - h := d.Height - for _, v := range ps { - if v.X >= x && - v.X < x+w && - v.Y >= y && - v.Y < y+h { - nps = append(nps, v) - } - } - return nps -} diff --git a/box.go b/box.go index 1dcfd86..561811f 100644 --- a/box.go +++ b/box.go @@ -4,114 +4,120 @@ package termui -type border struct { - X int - Y int - Width int - Height int - FgColor Attribute - BgColor Attribute +import "image" + +type Border struct { + Area image.Rectangle + Left bool + Top bool + Right bool + Bottom bool + Fg Attribute + Bg Attribute } -type hline struct { - X int - Y int - Length int - FgColor Attribute - BgColor Attribute +type Hline struct { + X int + Y int + Len int + Fg Attribute + Bg Attribute } -type vline struct { - X int - Y int - Length int - FgColor Attribute - BgColor Attribute +type Vline struct { + X int + Y int + Len int + Fg Attribute + Bg Attribute } -// Draw a horizontal line. -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].Ch = HORIZONTAL_LINE - pts[i].Bg = l.BgColor - pts[i].Fg = l.FgColor +// Buffer draws a horizontal line. +func (l Hline) Buffer() Buffer { + buf := NewBuffer() + for i := 0; i < l.Len; i++ { + buf.Set(l.X+i, l.Y, Cell{HORIZONTAL_LINE, l.Fg, l.Bg}) } - return pts + buf.Align() + return buf } -// Draw a vertical line. -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].Ch = VERTICAL_LINE - pts[i].Bg = l.BgColor - pts[i].Fg = l.FgColor +// Buffer draws a vertical line. +func (l Vline) Buffer() Buffer { + buf := NewBuffer() + for i := 0; i < l.Len; i++ { + buf.Set(l.X, l.Y+i, Cell{VERTICAL_LINE, l.Fg, l.Bg}) } - return pts + buf.Align() + return buf } -// Draw a box border. -func (b border) Buffer() []Point { - if b.Width < 2 || b.Height < 2 { - return nil - } - pts := make([]Point, 2*b.Width+2*b.Height-4) - - pts[0].X = b.X - pts[0].Y = b.Y - pts[0].Fg = b.FgColor - pts[0].Bg = b.BgColor - pts[0].Ch = TOP_LEFT - - pts[1].X = b.X + b.Width - 1 - pts[1].Y = b.Y - pts[1].Fg = b.FgColor - pts[1].Bg = b.BgColor - pts[1].Ch = TOP_RIGHT - - pts[2].X = b.X - pts[2].Y = b.Y + b.Height - 1 - pts[2].Fg = b.FgColor - pts[2].Bg = b.BgColor - pts[2].Ch = BOTTOM_LEFT - - pts[3].X = b.X + b.Width - 1 - pts[3].Y = b.Y + b.Height - 1 - pts[3].Fg = b.FgColor - pts[3].Bg = b.BgColor - pts[3].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()) - - return pts -} - -type labeledBorder struct { - border - Label string - LabelFgColor Attribute - LabelBgColor Attribute -} - -// Draw a box border with label. -func (lb labeledBorder) Buffer() []Point { - ps := lb.border.Buffer() - maxTxtW := lb.Width - 2 - rs := trimStr2Runes(lb.Label, maxTxtW) - - for i, j, w := 0, 0, 0; i < len(rs); i++ { - w = charWidth(rs[i]) - ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+j, lb.Y, lb.LabelFgColor, lb.LabelBgColor)) - j += w +// Buffer draws a box border. +func (b Border) Buffer() Buffer { + buf := NewBuffer() + if b.Area.Size().X < 2 || b.Area.Size().Y < 2 { + return buf } - return ps + min := b.Area.Min + max := b.Area.Max + + x0 := min.X + y0 := min.Y + x1 := max.X + y1 := max.Y + + // draw lines + switch { + case b.Top: + buf.Union(Hline{x0, y0, x1 - x0, b.Fg, b.Bg}.Buffer()) + fallthrough + case b.Bottom: + buf.Union(Hline{x0, y1, x1 - x0, b.Fg, b.Bg}.Buffer()) + fallthrough + case b.Left: + buf.Union(Vline{x0, y0, y1 - y0, b.Fg, b.Bg}.Buffer()) + fallthrough + case b.Right: + buf.Union(Vline{x1, y0, y1 - y0, b.Fg, b.Bg}.Buffer()) + } + + // draw corners + switch { + case b.Top && b.Left: + buf.Set(x0, y0, Cell{TOP_LEFT, b.Fg, b.Bg}) + fallthrough + case b.Top && b.Right: + buf.Set(x1, y0, Cell{TOP_RIGHT, b.Fg, b.Bg}) + fallthrough + case b.Bottom && b.Left: + buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.Fg, b.Bg}) + fallthrough + case b.Bottom && b.Right: + buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.Fg, b.Bg}) + } + + return buf +} + +// LabeledBorder defined label upon Border +type LabeledBorder struct { + Border + Label string + LabelFgClr Attribute + LabelBgClr Attribute +} + +// Buffer draw a box border with label. +func (lb LabeledBorder) Buffer() Buffer { + border := lb.Border.Buffer() + maxTxtW := lb.Area.Dx() + 1 - 2 + tx := DTrimTxCls(TextCells(lb.Label, lb.LabelFgClr, lb.LabelBgClr), maxTxtW) + + for i, w := 0, 0; i < len(tx); i++ { + border.Set(border.Area.Min.X+1+w, border.Area.Min.Y, tx[i]) + w += tx[i].Width() + } + + return border } diff --git a/buffer.go b/buffer.go new file mode 100644 index 0000000..2e0892f --- /dev/null +++ b/buffer.go @@ -0,0 +1,89 @@ +// Copyright 2015 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Cell is a rune with assigned Fg and Bg +type Cell struct { + Ch rune + Fg Attribute + Bg Attribute +} + +// Buffer is a renderable rectangle cell data container. +type Buffer struct { + Area image.Rectangle // selected drawing area + CellMap map[image.Point]Cell +} + +// At returns the cell at (x,y). +func (b Buffer) At(x, y int) Cell { + return b.CellMap[image.Pt(x, y)] +} + +// Set assigns a char to (x,y) +func (b Buffer) Set(x, y int, c Cell) { + b.CellMap[image.Pt(x, y)] = c +} + +// Bounds returns the domain for which At can return non-zero color. +func (b Buffer) Bounds() image.Rectangle { + x0, y0, x1, y1 := 0, 0, 0, 0 + for p := range b.CellMap { + switch { + case p.X > x1: + x1 = p.X + case p.X < x0: + x0 = p.X + case p.Y > y1: + y1 = p.Y + case p.Y < y0: + y0 = p.Y + } + } + return image.Rect(x0, y0, x1, y1) +} + +// Align sets drawing area to the buffer's bound +func (b *Buffer) Align() { + b.Area = b.Bounds() +} + +// NewCell returns a new cell +func NewCell(ch rune, fg, bg Attribute) Cell { + return Cell{ch, fg, bg} +} + +// Union squeezes buf into b +func (b Buffer) Union(buf Buffer) { + for p, c := range buf.CellMap { + b.Set(p.X, p.Y, c) + } +} + +// Union returns a new Buffer formed by squeezing bufs into one Buffer +func Union(bufs ...Buffer) Buffer { + buf := NewBuffer() + for _, b := range bufs { + buf.Union(b) + } + buf.Align() + return buf +} + +// Point for adapting use, will be removed after resolving bridging. +type Point struct { + X int + Y int + Ch rune + Fg Attribute + Bg Attribute +} + +// NewBuffer returns a new Buffer +func NewBuffer() Buffer { + return Buffer{CellMap: make(map[image.Point]Cell)} +} diff --git a/example/gauge.go b/example/gauge.go index 06e4946..f598d0e 100644 --- a/example/gauge.go +++ b/example/gauge.go @@ -7,6 +7,7 @@ package main import "github.com/gizak/termui" +import "github.com/gizak/termui/widget" func main() { err := termui.Init() @@ -17,16 +18,23 @@ func main() { termui.UseTheme("helloworld") - g0 := termui.NewGauge() + g0 := widget.NewGauge() g0.Percent = 40 g0.Width = 50 g0.Height = 3 g0.Border.Label = "Slim Gauge" g0.BarColor = termui.ColorRed - g0.Border.FgColor = termui.ColorWhite - g0.Border.LabelFgColor = termui.ColorCyan + g0.Border.Fg = termui.ColorWhite + g0.Border.LabelFgClr = termui.ColorCyan - g2 := termui.NewGauge() + gg := termui.NewBlock() + gg.Width = 50 + gg.Height = 5 + gg.Y = 12 + gg.Border.Label = "TEST" + gg.Align() + + g2 := widget.NewGauge() g2.Percent = 60 g2.Width = 50 g2.Height = 3 @@ -34,9 +42,9 @@ func main() { g2.Y = 3 g2.Border.Label = "Slim Gauge" g2.BarColor = termui.ColorYellow - g2.Border.FgColor = termui.ColorWhite + g2.Border.Fg = termui.ColorWhite - g1 := termui.NewGauge() + g1 := widget.NewGauge() g1.Percent = 30 g1.Width = 50 g1.Height = 5 @@ -44,10 +52,10 @@ func main() { g1.Border.Label = "Big Gauge" g1.PercentColor = termui.ColorYellow g1.BarColor = termui.ColorGreen - g1.Border.FgColor = termui.ColorWhite - g1.Border.LabelFgColor = termui.ColorMagenta + g1.Border.Fg = termui.ColorWhite + g1.Border.LabelFgClr = termui.ColorMagenta - termui.Render(g0, g1, g2) + termui.Render(g0, g1, g2, gg) <-termui.EventCh() } diff --git a/gauge.go b/gauge.go deleted file mode 100644 index 3e0003d..0000000 --- a/gauge.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2015 Zack Guo . All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -import "strconv" - -// Gauge is a progress bar like widget. -// A simple example: -/* - g := termui.NewGauge() - g.Percent = 40 - g.Width = 50 - g.Height = 3 - g.Border.Label = "Slim Gauge" - g.BarColor = termui.ColorRed - g.PercentColor = termui.ColorBlue -*/ -type Gauge struct { - Block - Percent int - BarColor Attribute - PercentColor Attribute -} - -// NewGauge return a new gauge with current theme. -func NewGauge() *Gauge { - g := &Gauge{ - Block: *NewBlock(), - PercentColor: theme.GaugePercent, - BarColor: theme.GaugeBar} - g.Width = 12 - g.Height = 5 - return g -} - -// Buffer implements Bufferer interface. -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.Ch = ' ' - p.Bg = g.BarColor - if p.Bg == ColorDefault { - p.Bg |= AttrReverse - } - ps = append(ps, p) - } - } - - // plot percentage - for i, v := range rs { - p := Point{} - p.X = prx + i - p.Y = pry - p.Ch = v - p.Fg = g.PercentColor - if w > g.innerWidth/2-1+i { - p.Bg = g.BarColor - if p.Bg == ColorDefault { - p.Bg |= AttrReverse - } - - } else { - p.Bg = g.Block.BgColor - } - ps = append(ps, p) - } - return g.Block.chopOverflow(ps) -} diff --git a/grid.go b/grid.go index 5f6e85e..1b15b92 100644 --- a/grid.go +++ b/grid.go @@ -160,8 +160,8 @@ func (r *Row) SetWidth(w int) { // Buffer implements Bufferer interface, // recursively merge all widgets buffer -func (r *Row) Buffer() []Point { - merged := []Point{} +func (r *Row) Buffer() Buffer { + merged := Buffer{} if r.isRenderableLeaf() { return r.Widget.Buffer() @@ -169,13 +169,13 @@ func (r *Row) Buffer() []Point { // for those are not leaves but have a renderable widget if r.Widget != nil { - merged = append(merged, r.Widget.Buffer()...) + merged.Union(r.Widget.Buffer()) } // collect buffer from children if !r.isLeaf() { for _, c := range r.Cols { - merged = append(merged, c.Buffer()...) + merged.Union(c.Buffer()) } } @@ -267,12 +267,13 @@ func (g *Grid) Align() { } // Buffer implments Bufferer interface. -func (g Grid) Buffer() []Point { - ps := []Point{} +func (g Grid) Buffer() Buffer { + buf := Buffer{} + for _, r := range g.Rows { - ps = append(ps, r.Buffer()...) + buf.Union(r.Buffer()) } - return ps + return buf } // Body corresponds to the entire terminal display region. diff --git a/helper.go b/helper.go index 86906c8..b73010c 100644 --- a/helper.go +++ b/helper.go @@ -148,3 +148,60 @@ func StringToAttribute(text string) Attribute { return result } + +// TextCells returns a coloured text cells []Cell +func TextCells(s string, fg, bg Attribute) []Cell { + cs := make([]Cell, 0, len(s)) + + // sequence := MarkdownTextRendererFactory{}.TextRenderer(s).Render(fg, bg) + // runes := []rune(sequence.NormalizedText) + runes := str2runes(s) + + for n := range runes { + // point, _ := sequence.PointAt(n, 0, 0) + // cs = append(cs, Cell{point.Ch, point.Fg, point.Bg}) + cs = append(cs, Cell{runes[n], fg, bg}) + } + return cs +} + +// Width returns the actual screen space the cell takes (usually 1 or 2). +func (c Cell) Width() int { + return charWidth(c.Ch) +} + +// Copy return a copy of c +func (c Cell) Copy() Cell { + return c +} + +// TrimTxCells trims the overflowed text cells sequence. +func TrimTxCells(cs []Cell, w int) []Cell { + if len(cs) <= w { + return cs + } + return cs[:w] +} + +// DTrimTxCls trims the overflowed text cells sequence and append dots at the end. +func DTrimTxCls(cs []Cell, w int) []Cell { + l := len(cs) + if l <= 0 { + return []Cell{} + } + + rt := make([]Cell, 0, w) + csw := 0 + for i := 0; i < l && csw <= w; i++ { + c := cs[i] + cw := c.Width() + + if cw+csw <= w { + rt = append(rt, c) + } else { + rt = append(rt, Cell{'…', c.Fg, c.Bg}) + } + } + + return rt +} diff --git a/point.go b/point.go deleted file mode 100644 index c381af9..0000000 --- a/point.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 Zack Guo . All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -// Point stands for a single cell in terminal. -type Point struct { - Ch rune - Bg Attribute - Fg Attribute - X int - Y int -} - -func newPoint(c rune, x, y int) (p Point) { - p.Ch = c - p.X = x - p.Y = y - return -} - -func newPointWithAttrs(c rune, x, y int, fg, bg Attribute) Point { - p := newPoint(c, x, y) - p.Bg = bg - p.Fg = fg - return p -} diff --git a/render.go b/render.go index 735fe5b..d1a5891 100644 --- a/render.go +++ b/render.go @@ -8,7 +8,7 @@ import tm "github.com/nsf/termbox-go" // Bufferer should be implemented by all renderable components. type Bufferer interface { - Buffer() []Point + Buffer() Buffer } // Init initializes termui library. This function should be called before any others. @@ -46,13 +46,18 @@ func TermHeight() int { // Render renders all Bufferer in the given order from left to right, // right could overlap on left ones. -func Render(rs ...Bufferer) { +func Render(bs ...Bufferer) { + // set tm bg tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg)) - for _, r := range rs { - buf := r.Buffer() - for _, v := range buf { - tm.SetCell(v.X, v.Y, v.Ch, toTmAttr(v.Fg), toTmAttr(v.Bg)) + for _, b := range bs { + buf := b.Buffer() + // set cels in buf + for p, c := range buf.CellMap { + if true { //}p.In(buf.Area) { + tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg)) + } } } + // render tm.Flush() } diff --git a/textRender.go b/textRender.go index 3cf5154..8a0f1ca 100644 --- a/textRender.go +++ b/textRender.go @@ -1,3 +1,5 @@ +// +build ignore + package termui import ( diff --git a/bar.go b/widget/barchart.go similarity index 99% rename from bar.go rename to widget/barchart.go index 57bae0a..a2e1934 100644 --- a/bar.go +++ b/widget/barchart.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/canvas.go b/widget/canvas.go similarity index 98% rename from canvas.go rename to widget/canvas.go index 614635e..295685a 100644 --- a/canvas.go +++ b/widget/canvas.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/canvas_test.go b/widget/canvas_test.go similarity index 97% rename from canvas_test.go rename to widget/canvas_test.go index 021949c..ca2a9e1 100644 --- a/canvas_test.go +++ b/widget/canvas_test.go @@ -1,3 +1,5 @@ +//+build ignore + package termui import ( diff --git a/widget/gauge.go b/widget/gauge.go new file mode 100644 index 0000000..9b02ed5 --- /dev/null +++ b/widget/gauge.go @@ -0,0 +1,67 @@ +// Copyright 2015 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package widget + +import "github.com/gizak/termui" +import "strconv" + +// Gauge is a progress bar like widget. +// A simple example: +/* + g := termui.NewGauge() + g.Percent = 40 + g.Width = 50 + g.Height = 3 + g.Border.Label = "Slim Gauge" + g.BarColor = termui.ColorRed + g.PercentColor = termui.ColorBlue +*/ +type Gauge struct { + termui.Block + Percent int + BarColor termui.Attribute + PercentColor termui.Attribute +} + +// NewGauge return a new gauge with current theme. +func NewGauge() *Gauge { + g := &Gauge{ + Block: *termui.NewBlock(), + PercentColor: termui.Theme().GaugePercent, + BarColor: termui.Theme().GaugeBar} + g.Width = 12 + g.Height = 3 + return g +} + +// Buffer implements Bufferer interface. +func (g *Gauge) Buffer() termui.Buffer { + buf := g.Block.Buffer() + + inner := g.InnerBounds() + w := g.Percent * (inner.Dx() + 1) / 100 + s := strconv.Itoa(g.Percent) + "%" + tx := termui.TextCells(s, g.PercentColor, g.Bg) + + prx := inner.Min.X + (inner.Dx()+1)/2 - 1 + pry := inner.Min.Y + (inner.Dy()+1)/2 + + // plot bar + for i := 0; i <= inner.Dy(); i++ { + for j := 0; j < w; j++ { + c := termui.Cell{' ', g.BarColor, g.BarColor} + buf.Set(inner.Min.X+j, inner.Min.Y+i, c) + } + } + + // plot percentage + for i, v := range tx { + if w > (inner.Dx()+1)/2-1+i { + v.Bg = g.BarColor + } + buf.Set(prx+i, pry, v) + } + return buf +} diff --git a/chart.go b/widget/linechart.go similarity index 99% rename from chart.go rename to widget/linechart.go index d6fb8bc..b3c349f 100644 --- a/chart.go +++ b/widget/linechart.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/chart_others.go b/widget/linechart_others.go similarity index 93% rename from chart_others.go rename to widget/linechart_others.go index 8911873..eff3158 100644 --- a/chart_others.go +++ b/widget/linechart_others.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/chart_windows.go b/widget/linechart_windows.go similarity index 93% rename from chart_windows.go rename to widget/linechart_windows.go index 9f9a5e9..8cbc5cd 100644 --- a/chart_windows.go +++ b/widget/linechart_windows.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/list.go b/widget/list.go similarity index 99% rename from list.go rename to widget/list.go index e6e5f11..ad2b5d5 100644 --- a/list.go +++ b/widget/list.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/p.go b/widget/par.go similarity index 98% rename from p.go rename to widget/par.go index b6237fb..230b659 100644 --- a/p.go +++ b/widget/par.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file. diff --git a/sparkline.go b/widget/sparkline.go similarity index 99% rename from sparkline.go rename to widget/sparkline.go index e0725d5..809b9e6 100644 --- a/sparkline.go +++ b/widget/sparkline.go @@ -1,3 +1,5 @@ +// +build ignore + // Copyright 2015 Zack Guo . All rights reserved. // Use of this source code is governed by a MIT license that can // be found in the LICENSE file.