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
This commit is contained in:
gizak 2015-04-21 09:56:10 -04:00
parent 0a29dad7e7
commit 7aed750f64
20 changed files with 428 additions and 298 deletions

124
block.go
View File

@ -4,22 +4,22 @@
package termui package termui
import "image"
// Block is a base struct for all other upper level widgets, // Block is a base struct for all other upper level widgets,
// consider it as css: display:block. // consider it as css: display:block.
// Normally you do not need to create it manually. // Normally you do not need to create it manually.
type Block struct { type Block struct {
Area image.Rectangle
innerArea image.Rectangle
X int X int
Y int Y int
Border labeledBorder Border LabeledBorder
IsDisplay bool IsDisplay bool
HasBorder bool HasBorder bool
BgColor Attribute Bg Attribute
Width int Width int
Height int Height int
innerWidth int
innerHeight int
innerX int
innerY int
PaddingTop int PaddingTop int
PaddingBottom int PaddingBottom int
PaddingLeft int PaddingLeft int
@ -31,75 +31,81 @@ func NewBlock() *Block {
d := Block{} d := Block{}
d.IsDisplay = true d.IsDisplay = true
d.HasBorder = theme.HasBorder d.HasBorder = theme.HasBorder
d.Border.BgColor = theme.BorderBg d.Border.Left = true
d.Border.FgColor = theme.BorderFg d.Border.Right = true
d.Border.LabelBgColor = theme.BorderLabelTextBg d.Border.Top = true
d.Border.LabelFgColor = theme.BorderLabelTextFg d.Border.Bottom = true
d.BgColor = theme.BlockBg 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.Width = 2
d.Height = 2 d.Height = 2
return &d return &d
} }
// compute box model // Align computes box model
func (d *Block) align() { func (d *Block) Align() {
d.innerWidth = d.Width - d.PaddingLeft - d.PaddingRight d.Area.Min.X = d.X
d.innerHeight = d.Height - d.PaddingTop - d.PaddingBottom d.Area.Min.Y = d.Y
d.innerX = d.X + d.PaddingLeft d.Area.Max.X = d.X + d.Width - 1
d.innerY = d.Y + d.PaddingTop 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 { if d.HasBorder {
d.innerHeight -= 2 switch {
d.innerWidth -= 2 case d.Border.Left:
d.Border.X = d.X d.innerArea.Min.X++
d.Border.Y = d.Y fallthrough
d.Border.Width = d.Width case d.Border.Right:
d.Border.Height = d.Height d.innerArea.Max.X--
d.innerX++ fallthrough
d.innerY++ 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 // InnerBounds returns the internal bounds of the block after aligning and
// calculating the padding and border, if any. // calculating the padding and border, if any.
func (d *Block) InnerBounds() (x, y, width, height int) { func (d *Block) InnerBounds() image.Rectangle {
d.align() d.Align()
return d.innerX, d.innerY, d.innerWidth, d.innerHeight return d.innerArea
} }
// Buffer implements Bufferer interface. // Buffer implements Bufferer interface.
// Draw background and border (if any). // Draw background and border (if any).
func (d *Block) Buffer() []Point { func (d *Block) Buffer() Buffer {
d.align() d.Align()
ps := []Point{} buf := NewBuffer()
buf.Area = d.Area
if !d.IsDisplay { if !d.IsDisplay {
return ps return buf
} }
// render border
if d.HasBorder { if d.HasBorder {
ps = d.Border.Buffer() buf.Union(d.Border.Buffer())
} }
for i := 0; i < d.innerWidth; i++ { // render background
for j := 0; j < d.innerHeight; j++ { for p := range buf.CellMap {
p := Point{} if p.In(d.innerArea) {
p.X = d.X + 1 + i buf.CellMap[p] = Cell{' ', ColorDefault, d.Bg}
p.Y = d.Y + 1 + j
p.Ch = ' '
p.Bg = d.BgColor
ps = append(ps, p)
} }
} }
return ps return buf
} }
// GetHeight implements GridBufferer. // GetHeight implements GridBufferer.
@ -122,21 +128,3 @@ func (d *Block) SetY(y int) {
func (d *Block) SetWidth(w int) { func (d *Block) SetWidth(w int) {
d.Width = w 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
}

198
box.go
View File

@ -4,114 +4,120 @@
package termui package termui
type border struct { import "image"
X int
Y int type Border struct {
Width int Area image.Rectangle
Height int Left bool
FgColor Attribute Top bool
BgColor Attribute Right bool
Bottom bool
Fg Attribute
Bg Attribute
} }
type hline struct { type Hline struct {
X int X int
Y int Y int
Length int Len int
FgColor Attribute Fg Attribute
BgColor Attribute Bg Attribute
} }
type vline struct { type Vline struct {
X int X int
Y int Y int
Length int Len int
FgColor Attribute Fg Attribute
BgColor Attribute Bg Attribute
} }
// Draw a horizontal line. // Buffer draws a horizontal line.
func (l hline) Buffer() []Point { func (l Hline) Buffer() Buffer {
pts := make([]Point, l.Length) buf := NewBuffer()
for i := 0; i < l.Length; i++ { for i := 0; i < l.Len; i++ {
pts[i].X = l.X + i buf.Set(l.X+i, l.Y, Cell{HORIZONTAL_LINE, l.Fg, l.Bg})
pts[i].Y = l.Y
pts[i].Ch = HORIZONTAL_LINE
pts[i].Bg = l.BgColor
pts[i].Fg = l.FgColor
} }
return pts buf.Align()
return buf
} }
// Draw a vertical line. // Buffer draws a vertical line.
func (l vline) Buffer() []Point { func (l Vline) Buffer() Buffer {
pts := make([]Point, l.Length) buf := NewBuffer()
for i := 0; i < l.Length; i++ { for i := 0; i < l.Len; i++ {
pts[i].X = l.X buf.Set(l.X, l.Y+i, Cell{VERTICAL_LINE, l.Fg, l.Bg})
pts[i].Y = l.Y + i
pts[i].Ch = VERTICAL_LINE
pts[i].Bg = l.BgColor
pts[i].Fg = l.FgColor
} }
return pts buf.Align()
return buf
} }
// Draw a box border. // Buffer draws a box border.
func (b border) Buffer() []Point { func (b Border) Buffer() Buffer {
if b.Width < 2 || b.Height < 2 { buf := NewBuffer()
return nil if b.Area.Size().X < 2 || b.Area.Size().Y < 2 {
} return buf
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
} }
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
} }

89
buffer.go Normal file
View File

@ -0,0 +1,89 @@
// Copyright 2015 Zack Guo <gizak@icloud.com>. 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)}
}

View File

@ -7,6 +7,7 @@
package main package main
import "github.com/gizak/termui" import "github.com/gizak/termui"
import "github.com/gizak/termui/widget"
func main() { func main() {
err := termui.Init() err := termui.Init()
@ -17,16 +18,23 @@ func main() {
termui.UseTheme("helloworld") termui.UseTheme("helloworld")
g0 := termui.NewGauge() g0 := widget.NewGauge()
g0.Percent = 40 g0.Percent = 40
g0.Width = 50 g0.Width = 50
g0.Height = 3 g0.Height = 3
g0.Border.Label = "Slim Gauge" g0.Border.Label = "Slim Gauge"
g0.BarColor = termui.ColorRed g0.BarColor = termui.ColorRed
g0.Border.FgColor = termui.ColorWhite g0.Border.Fg = termui.ColorWhite
g0.Border.LabelFgColor = termui.ColorCyan 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.Percent = 60
g2.Width = 50 g2.Width = 50
g2.Height = 3 g2.Height = 3
@ -34,9 +42,9 @@ func main() {
g2.Y = 3 g2.Y = 3
g2.Border.Label = "Slim Gauge" g2.Border.Label = "Slim Gauge"
g2.BarColor = termui.ColorYellow g2.BarColor = termui.ColorYellow
g2.Border.FgColor = termui.ColorWhite g2.Border.Fg = termui.ColorWhite
g1 := termui.NewGauge() g1 := widget.NewGauge()
g1.Percent = 30 g1.Percent = 30
g1.Width = 50 g1.Width = 50
g1.Height = 5 g1.Height = 5
@ -44,10 +52,10 @@ func main() {
g1.Border.Label = "Big Gauge" g1.Border.Label = "Big Gauge"
g1.PercentColor = termui.ColorYellow g1.PercentColor = termui.ColorYellow
g1.BarColor = termui.ColorGreen g1.BarColor = termui.ColorGreen
g1.Border.FgColor = termui.ColorWhite g1.Border.Fg = termui.ColorWhite
g1.Border.LabelFgColor = termui.ColorMagenta g1.Border.LabelFgClr = termui.ColorMagenta
termui.Render(g0, g1, g2) termui.Render(g0, g1, g2, gg)
<-termui.EventCh() <-termui.EventCh()
} }

View File

@ -1,83 +0,0 @@
// Copyright 2015 Zack Guo <gizak@icloud.com>. 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)
}

17
grid.go
View File

@ -160,8 +160,8 @@ func (r *Row) SetWidth(w int) {
// Buffer implements Bufferer interface, // Buffer implements Bufferer interface,
// recursively merge all widgets buffer // recursively merge all widgets buffer
func (r *Row) Buffer() []Point { func (r *Row) Buffer() Buffer {
merged := []Point{} merged := Buffer{}
if r.isRenderableLeaf() { if r.isRenderableLeaf() {
return r.Widget.Buffer() return r.Widget.Buffer()
@ -169,13 +169,13 @@ func (r *Row) Buffer() []Point {
// for those are not leaves but have a renderable widget // for those are not leaves but have a renderable widget
if r.Widget != nil { if r.Widget != nil {
merged = append(merged, r.Widget.Buffer()...) merged.Union(r.Widget.Buffer())
} }
// collect buffer from children // collect buffer from children
if !r.isLeaf() { if !r.isLeaf() {
for _, c := range r.Cols { 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. // Buffer implments Bufferer interface.
func (g Grid) Buffer() []Point { func (g Grid) Buffer() Buffer {
ps := []Point{} buf := Buffer{}
for _, r := range g.Rows { 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. // Body corresponds to the entire terminal display region.

View File

@ -148,3 +148,60 @@ func StringToAttribute(text string) Attribute {
return result 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
}

View File

@ -1,28 +0,0 @@
// Copyright 2015 Zack Guo <gizak@icloud.com>. 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
}

View File

@ -8,7 +8,7 @@ import tm "github.com/nsf/termbox-go"
// Bufferer should be implemented by all renderable components. // Bufferer should be implemented by all renderable components.
type Bufferer interface { type Bufferer interface {
Buffer() []Point Buffer() Buffer
} }
// Init initializes termui library. This function should be called before any others. // 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, // Render renders all Bufferer in the given order from left to right,
// right could overlap on left ones. // right could overlap on left ones.
func Render(rs ...Bufferer) { func Render(bs ...Bufferer) {
// set tm bg
tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg)) tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg))
for _, r := range rs { for _, b := range bs {
buf := r.Buffer() buf := b.Buffer()
for _, v := range buf { // set cels in buf
tm.SetCell(v.X, v.Y, v.Ch, toTmAttr(v.Fg), toTmAttr(v.Bg)) 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() tm.Flush()
} }

View File

@ -1,3 +1,5 @@
// +build ignore
package termui package termui
import ( import (

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//+build ignore
package termui package termui
import ( import (

67
widget/gauge.go Normal file
View File

@ -0,0 +1,67 @@
// Copyright 2015 Zack Guo <gizak@icloud.com>. 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
}

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
// +build ignore
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. // Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can // Use of this source code is governed by a MIT license that can
// be found in the LICENSE file. // be found in the LICENSE file.