diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-06-25 23:18:42 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-06-25 23:18:42 +0800 |
commit | b0a5be4495962c291a25cbea793e43bad0781510 (patch) | |
tree | e058201e7b29c3cb8efd710e20ec1e1f97f64368 /Godeps/_workspace/src/github.com/gizak/termui | |
parent | e64625aa8215985c85f97f914a98db081e07714f (diff) | |
parent | e9c0b5431cbd7430ddec9fd17983241018fd8a55 (diff) | |
download | go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.tar go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.tar.gz go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.tar.bz2 go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.tar.lz go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.tar.xz go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.tar.zst go-tangerine-b0a5be4495962c291a25cbea793e43bad0781510.zip |
Merge pull request #1321 from karalabe/cut-it-open-3000
Metrics collecting and reporting support
Diffstat (limited to 'Godeps/_workspace/src/github.com/gizak/termui')
51 files changed, 3584 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/.gitignore b/Godeps/_workspace/src/github.com/gizak/termui/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/Godeps/_workspace/src/github.com/gizak/termui/.travis.yml b/Godeps/_workspace/src/github.com/gizak/termui/.travis.yml new file mode 100644 index 000000000..206e88740 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - tip + +script: go test -v ./
\ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/gizak/termui/LICENSE b/Godeps/_workspace/src/github.com/gizak/termui/LICENSE new file mode 100644 index 000000000..311ccc74f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Zack Guo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Godeps/_workspace/src/github.com/gizak/termui/README.md b/Godeps/_workspace/src/github.com/gizak/termui/README.md new file mode 100644 index 000000000..b9bc3024d --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/README.md @@ -0,0 +1,159 @@ +# termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui) + +## Update 23/06/2015 +Pull requests and master branch are freezing, waiting for merging from `refactoring` branch. + +## Notice +termui comes with ABSOLUTELY NO WARRANTY, and there is a breaking change coming up (see refactoring branch) which will change the `Bufferer` interface and many others. These changes reduce calculation overhead and introduce a new drawing buffer with better capacibilities. We will step into the next stage (call it beta) after merging these changes. + +## Introduction +Go terminal dashboard. Inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go. + +Cross-platform, easy to compile, and fully-customizable. + +__Demo:__ (cast under osx 10.10; Terminal.app; Menlo Regular 12pt.) + +<img src="./example/dashboard.gif" alt="demo" width="600"> + +__Grid layout:__ + +Expressive syntax, using [12 columns grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) +```go + import ui "github.com/gizak/termui" + // init and create widgets... + + // build + ui.Body.AddRows( + ui.NewRow( + ui.NewCol(6, 0, widget0), + ui.NewCol(6, 0, widget1)), + ui.NewRow( + ui.NewCol(3, 0, widget2), + ui.NewCol(3, 0, widget30, widget31, widget32), + ui.NewCol(6, 0, widget4))) + + // calculate layout + ui.Body.Align() + + ui.Render(ui.Body) +``` +[demo code:](https://github.com/gizak/termui/blob/master/example/grid.go) + +<img src="./example/grid.gif" alt="grid" width="500"> + +## Installation + + go get github.com/gizak/termui + +## Usage + +Each component's layout is a bit like HTML block (box model), which has border and padding. + +The `Border` property can be chosen to hide or display (with its border label), when it comes to display, the label takes 1 padding space (i.e. in css: `padding: 1;`, innerHeight and innerWidth therefore shrunk by 1). + +`````go + import ui "github.com/gizak/termui" // <- ui shortcut, optional + + func main() { + err := ui.Init() + if err != nil { + panic(err) + } + defer ui.Close() + + p := ui.NewPar(":PRESS q TO QUIT DEMO") + p.Height = 3 + p.Width = 50 + p.TextFgColor = ui.ColorWhite + p.Border.Label = "Text Box" + p.Border.FgColor = ui.ColorCyan + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.Height = 3 + g.Y = 11 + g.Border.Label = "Gauge" + g.BarColor = ui.ColorRed + g.Border.FgColor = ui.ColorWhite + g.Border.LabelFgColor = ui.ColorCyan + + ui.Render(p, g) + + // event handler... + } +````` + +Note that components can be overlapped (I'd rather call this a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right). + +## Themes + +_All_ colors in _all_ components can be changed at _any_ time, while there provides some predefined color schemes: + +```go +// for now there are only two themes: default and helloworld +termui.UseTheme("helloworld") + +// create components... +``` +The `default ` theme's settings depend on the user's terminal color scheme, which is saying if your terminal default font color is white and background is white, it will be like: + +<img src="./example/themedefault.png" alt="default" type="image/png" width="600"> + +The `helloworld` color scheme drops in some colors! + +<img src="./example/themehelloworld.png" alt="helloworld" type="image/png" width="600"> + +## Widgets + +#### Par + +[demo code](https://github.com/gizak/termui/blob/master/example/par.go) + +<img src="./example/par.png" alt="par" type="image/png" width="300"> + +#### List +[demo code](https://github.com/gizak/termui/blob/master/example/list.go) + +<img src="./example/list.png" alt="list" type="image/png" width="200"> + +#### Gauge +[demo code](https://github.com/gizak/termui/blob/master/example/gauge.go) + +<img src="./example/gauge.png" alt="gauge" type="image/png" width="350"> + +#### Line Chart +[demo code](https://github.com/gizak/termui/blob/master/example/linechart.go) + +<img src="./example/linechart.png" alt="linechart" type="image/png" width="450"> + +#### Bar Chart +[demo code](https://github.com/gizak/termui/blob/master/example/barchart.go) + +<img src="./example/barchart.png" alt="barchart" type="image/png" width="150"> + +#### Mult-Bar / Stacked-Bar Chart +[demo code](https://github.com/gizak/termui/blob/master/example/mbarchart.go) + +<img src="./example/mbarchart.png" alt="barchart" type="image/png" width="150"> + +#### Sparklines +[demo code](https://github.com/gizak/termui/blob/master/example/sparklines.go) + +<img src="./example/sparklines.png" alt="sparklines" type="image/png" width="350"> + + +## GoDoc + +[godoc](https://godoc.org/github.com/gizak/termui) + +## TODO + +- [x] Grid layout +- [ ] Event system +- [ ] Canvas widget +- [ ] Refine APIs +- [ ] Focusable widgets + +## License +This library is under the [MIT License](http://opensource.org/licenses/MIT) diff --git a/Godeps/_workspace/src/github.com/gizak/termui/bar.go b/Godeps/_workspace/src/github.com/gizak/termui/bar.go new file mode 100644 index 000000000..57bae0ae8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/bar.go @@ -0,0 +1,135 @@ +// 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 "fmt" + +// BarChart creates multiple bars in a widget: +/* + bc := termui.NewBarChart() + data := []int{3, 2, 5, 3, 9, 5} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.Border.Label = "Bar Chart" + bc.Data = data + bc.Width = 26 + bc.Height = 10 + bc.DataLabels = bclabels + bc.TextColor = termui.ColorGreen + bc.BarColor = termui.ColorRed + bc.NumColor = termui.ColorYellow +*/ +type BarChart struct { + Block + BarColor Attribute + TextColor Attribute + NumColor Attribute + Data []int + DataLabels []string + BarWidth int + BarGap int + labels [][]rune + dataNum [][]rune + numBar int + scale float64 + max int +} + +// NewBarChart returns a new *BarChart with current theme. +func NewBarChart() *BarChart { + bc := &BarChart{Block: *NewBlock()} + bc.BarColor = theme.BarChartBar + bc.NumColor = theme.BarChartNum + bc.TextColor = theme.BarChartText + bc.BarGap = 1 + bc.BarWidth = 3 + return bc +} + +func (bc *BarChart) layout() { + bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth) + bc.labels = make([][]rune, bc.numBar) + bc.dataNum = make([][]rune, len(bc.Data)) + + for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ { + bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth) + n := bc.Data[i] + s := fmt.Sprint(n) + bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth) + } + + //bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range + // Asign a negative value to get maxvalue auto-populates + if bc.max == 0 { + bc.max = -1 + } + for i := 0; i < len(bc.Data); i++ { + if bc.max < bc.Data[i] { + bc.max = bc.Data[i] + } + } + bc.scale = float64(bc.max) / float64(bc.innerHeight-1) +} + +func (bc *BarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *BarChart) Buffer() []Point { + ps := bc.Block.Buffer() + bc.layout() + + for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ { + h := int(float64(bc.Data[i]) / bc.scale) + oftX := i * (bc.BarWidth + bc.BarGap) + // plot bar + for j := 0; j < bc.BarWidth; j++ { + for k := 0; k < h; k++ { + p := Point{} + p.Ch = ' ' + p.Bg = bc.BarColor + if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent! + p.Bg |= AttrReverse + } + p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j + p.Y = bc.innerY + bc.innerHeight - 2 - k + ps = append(ps, p) + } + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + p := Point{} + p.Ch = bc.labels[i][j] + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + bc.innerHeight - 1 + p.X = bc.innerX + oftX + k + ps = append(ps, p) + k += w + } + // plot num + for j := 0; j < len(bc.dataNum[i]); j++ { + p := Point{} + p.Ch = bc.dataNum[i][j] + p.Fg = bc.NumColor + p.Bg = bc.BarColor + if bc.BarColor == ColorDefault { // the same as above + p.Bg |= AttrReverse + } + if h == 0 { + p.Bg = bc.BgColor + } + p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j + p.Y = bc.innerY + bc.innerHeight - 2 + ps = append(ps, p) + } + } + + return bc.Block.chopOverflow(ps) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/block.go b/Godeps/_workspace/src/github.com/gizak/termui/block.go new file mode 100644 index 000000000..953136596 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/block.go @@ -0,0 +1,142 @@ +// 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 + +// 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 { + X int + Y int + Border labeledBorder + IsDisplay bool + HasBorder bool + BgColor Attribute + Width int + Height int + innerWidth int + innerHeight int + innerX int + innerY int + PaddingTop int + PaddingBottom int + PaddingLeft int + PaddingRight int +} + +// NewBlock returns a *Block which inherits styles from current theme. +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.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 + + 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++ + } + + 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 +} + +// Buffer implements Bufferer interface. +// Draw background and border (if any). +func (d *Block) Buffer() []Point { + d.align() + + ps := []Point{} + if !d.IsDisplay { + return ps + } + + if d.HasBorder { + ps = 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) + } + } + return ps +} + +// GetHeight implements GridBufferer. +// It returns current height of the block. +func (d Block) GetHeight() int { + return d.Height +} + +// SetX implements GridBufferer interface, which sets block's x position. +func (d *Block) SetX(x int) { + d.X = x +} + +// SetY implements GridBufferer interface, it sets y position for block. +func (d *Block) SetY(y int) { + d.Y = y +} + +// SetWidth implements GridBuffer interface, it sets block's width. +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/Godeps/_workspace/src/github.com/gizak/termui/block_test.go b/Godeps/_workspace/src/github.com/gizak/termui/block_test.go new file mode 100644 index 000000000..2de205b21 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/block_test.go @@ -0,0 +1,46 @@ +package termui + +import "testing" + +func TestBlock_InnerBounds(t *testing.T) { + b := NewBlock() + b.X = 10 + b.Y = 11 + b.Width = 12 + b.Height = 13 + + assert := func(name string, x, y, w, h int) { + t.Log(name) + cx, cy, cw, ch := b.InnerBounds() + if cx != x { + t.Errorf("expected x to be %d but got %d", x, cx) + } + if cy != y { + t.Errorf("expected y to be %d but got %d", y, cy) + } + if cw != w { + t.Errorf("expected width to be %d but got %d", w, cw) + } + if ch != h { + t.Errorf("expected height to be %d but got %d", h, ch) + } + } + + b.HasBorder = false + assert("no border, no padding", 10, 11, 12, 13) + + b.HasBorder = true + assert("border, no padding", 11, 12, 10, 11) + + b.PaddingBottom = 2 + assert("border, 2b padding", 11, 12, 10, 9) + + b.PaddingTop = 3 + assert("border, 2b 3t padding", 11, 15, 10, 6) + + b.PaddingLeft = 4 + assert("border, 2b 3t 4l padding", 15, 15, 6, 6) + + b.PaddingRight = 5 + assert("border, 2b 3t 4l 5r padding", 15, 15, 1, 6) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box.go b/Godeps/_workspace/src/github.com/gizak/termui/box.go new file mode 100644 index 000000000..1dcfd8692 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/box.go @@ -0,0 +1,117 @@ +// 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 + +type border struct { + X int + Y int + Width int + Height int + FgColor Attribute + BgColor Attribute +} + +type hline struct { + X int + Y int + Length int + FgColor Attribute + BgColor Attribute +} + +type vline struct { + X int + Y int + Length int + FgColor Attribute + BgColor 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 + } + return pts +} + +// 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 + } + return pts +} + +// 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 + } + + return ps +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box_others.go b/Godeps/_workspace/src/github.com/gizak/termui/box_others.go new file mode 100644 index 000000000..bcc3d7ded --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/box_others.go @@ -0,0 +1,14 @@ +// 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. + +// +build !windows + +package termui + +const TOP_RIGHT = '┐' +const VERTICAL_LINE = '│' +const HORIZONTAL_LINE = '─' +const TOP_LEFT = '┌' +const BOTTOM_RIGHT = '┘' +const BOTTOM_LEFT = '└' diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go b/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go new file mode 100644 index 000000000..dd39019fe --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go @@ -0,0 +1,14 @@ +// 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. + +// +build windows + +package termui + +const TOP_RIGHT = '+' +const VERTICAL_LINE = '|' +const HORIZONTAL_LINE = '-' +const TOP_LEFT = '+' +const BOTTOM_RIGHT = '+' +const BOTTOM_LEFT = '+' diff --git a/Godeps/_workspace/src/github.com/gizak/termui/canvas.go b/Godeps/_workspace/src/github.com/gizak/termui/canvas.go new file mode 100644 index 000000000..614635ee4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/canvas.go @@ -0,0 +1,74 @@ +// 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 + +/* +dots: + ,___, + |1 4| + |2 5| + |3 6| + |7 8| + ````` +*/ + +var brailleBase = '\u2800' + +var brailleOftMap = [4][2]rune{ + {'\u0001', '\u0008'}, + {'\u0002', '\u0010'}, + {'\u0004', '\u0020'}, + {'\u0040', '\u0080'}} + +// Canvas contains drawing map: i,j -> rune +type Canvas map[[2]int]rune + +// NewCanvas returns an empty Canvas +func NewCanvas() Canvas { + return make(map[[2]int]rune) +} + +func chOft(x, y int) rune { + return brailleOftMap[y%4][x%2] +} + +func (c Canvas) rawCh(x, y int) rune { + if ch, ok := c[[2]int{x, y}]; ok { + return ch + } + return '\u0000' //brailleOffset +} + +// return coordinate in terminal +func chPos(x, y int) (int, int) { + return y / 4, x / 2 +} + +// Set sets a point (x,y) in the virtual coordinate +func (c Canvas) Set(x, y int) { + i, j := chPos(x, y) + ch := c.rawCh(i, j) + ch |= chOft(x, y) + c[[2]int{i, j}] = ch +} + +// Unset removes point (x,y) +func (c Canvas) Unset(x, y int) { + i, j := chPos(x, y) + ch := c.rawCh(i, j) + ch &= ^chOft(x, y) + c[[2]int{i, j}] = ch +} + +// Buffer returns un-styled points +func (c Canvas) Buffer() []Point { + ps := make([]Point, len(c)) + i := 0 + for k, v := range c { + ps[i] = newPoint(v+brailleBase, k[0], k[1]) + i++ + } + return ps +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go b/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go new file mode 100644 index 000000000..021949ced --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go @@ -0,0 +1,55 @@ +package termui + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestCanvasSet(t *testing.T) { + c := NewCanvas() + c.Set(0, 0) + c.Set(0, 1) + c.Set(0, 2) + c.Set(0, 3) + c.Set(1, 3) + c.Set(2, 3) + c.Set(3, 3) + c.Set(4, 3) + c.Set(5, 3) + spew.Dump(c) +} + +func TestCanvasUnset(t *testing.T) { + c := NewCanvas() + c.Set(0, 0) + c.Set(0, 1) + c.Set(0, 2) + c.Unset(0, 2) + spew.Dump(c) + c.Unset(0, 3) + spew.Dump(c) +} + +func TestCanvasBuffer(t *testing.T) { + c := NewCanvas() + c.Set(0, 0) + c.Set(0, 1) + c.Set(0, 2) + c.Set(0, 3) + c.Set(1, 3) + c.Set(2, 3) + c.Set(3, 3) + c.Set(4, 3) + c.Set(5, 3) + c.Set(6, 3) + c.Set(7, 2) + c.Set(8, 1) + c.Set(9, 0) + bufs := c.Buffer() + rs := make([]rune, len(bufs)) + for i, v := range bufs { + rs[i] = v.Ch + } + spew.Dump(string(rs)) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart.go b/Godeps/_workspace/src/github.com/gizak/termui/chart.go new file mode 100644 index 000000000..d6fb8bc7d --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/chart.go @@ -0,0 +1,336 @@ +// 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 ( + "fmt" + "math" +) + +// only 16 possible combinations, why bother +var braillePatterns = map[[2]int]rune{ + [2]int{0, 0}: '⣀', + [2]int{0, 1}: '⡠', + [2]int{0, 2}: '⡐', + [2]int{0, 3}: '⡈', + + [2]int{1, 0}: '⢄', + [2]int{1, 1}: '⠤', + [2]int{1, 2}: '⠔', + [2]int{1, 3}: '⠌', + + [2]int{2, 0}: '⢂', + [2]int{2, 1}: '⠢', + [2]int{2, 2}: '⠒', + [2]int{2, 3}: '⠊', + + [2]int{3, 0}: '⢁', + [2]int{3, 1}: '⠡', + [2]int{3, 2}: '⠑', + [2]int{3, 3}: '⠉', +} + +var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'} +var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'} + +// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode, +// because one braille char can represent two data points. +/* + lc := termui.NewLineChart() + lc.Border.Label = "braille-mode Line Chart" + lc.Data = [1.2, 1.3, 1.5, 1.7, 1.5, 1.6, 1.8, 2.0] + lc.Width = 50 + lc.Height = 12 + lc.AxesColor = termui.ColorWhite + lc.LineColor = termui.ColorGreen | termui.AttrBold + // termui.Render(lc)... +*/ +type LineChart struct { + Block + Data []float64 + DataLabels []string // if unset, the data indices will be used + Mode string // braille | dot + DotStyle rune + LineColor Attribute + scale float64 // data span per cell on y-axis + AxesColor Attribute + drawingX int + drawingY int + axisYHeight int + axisXWidth int + axisYLebelGap int + axisXLebelGap int + topValue float64 + bottomValue float64 + labelX [][]rune + labelY [][]rune + labelYSpace int + maxY float64 + minY float64 +} + +// NewLineChart returns a new LineChart with current theme. +func NewLineChart() *LineChart { + lc := &LineChart{Block: *NewBlock()} + lc.AxesColor = theme.LineChartAxes + lc.LineColor = theme.LineChartLine + lc.Mode = "braille" + lc.DotStyle = '•' + lc.axisXLebelGap = 2 + lc.axisYLebelGap = 1 + lc.bottomValue = math.Inf(1) + lc.topValue = math.Inf(-1) + return lc +} + +// one cell contains two data points +// so the capicity is 2x as dot-mode +func (lc *LineChart) renderBraille() []Point { + ps := []Point{} + + // return: b -> which cell should the point be in + // m -> in the cell, divided into 4 equal height levels, which subcell? + getPos := func(d float64) (b, m int) { + cnt4 := int((d-lc.bottomValue)/(lc.scale/4) + 0.5) + b = cnt4 / 4 + m = cnt4 % 4 + return + } + // plot points + for i := 0; 2*i+1 < len(lc.Data) && i < lc.axisXWidth; i++ { + b0, m0 := getPos(lc.Data[2*i]) + b1, m1 := getPos(lc.Data[2*i+1]) + + if b0 == b1 { + p := Point{} + p.Ch = braillePatterns[[2]int{m0, m1}] + p.Bg = lc.BgColor + p.Fg = lc.LineColor + p.Y = lc.innerY + lc.innerHeight - 3 - b0 + p.X = lc.innerX + lc.labelYSpace + 1 + i + ps = append(ps, p) + } else { + p0 := newPointWithAttrs(lSingleBraille[m0], + lc.innerX+lc.labelYSpace+1+i, + lc.innerY+lc.innerHeight-3-b0, + lc.LineColor, + lc.BgColor) + p1 := newPointWithAttrs(rSingleBraille[m1], + lc.innerX+lc.labelYSpace+1+i, + lc.innerY+lc.innerHeight-3-b1, + lc.LineColor, + lc.BgColor) + ps = append(ps, p0, p1) + } + + } + return ps +} + +func (lc *LineChart) renderDot() []Point { + ps := []Point{} + for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ { + p := Point{} + p.Ch = lc.DotStyle + p.Fg = lc.LineColor + p.Bg = lc.BgColor + p.X = lc.innerX + lc.labelYSpace + 1 + i + p.Y = lc.innerY + lc.innerHeight - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5) + ps = append(ps, p) + } + + return ps +} + +func (lc *LineChart) calcLabelX() { + lc.labelX = [][]rune{} + + for i, l := 0, 0; i < len(lc.DataLabels) && l < lc.axisXWidth; i++ { + if lc.Mode == "dot" { + if l >= len(lc.DataLabels) { + break + } + + s := str2runes(lc.DataLabels[l]) + w := strWidth(lc.DataLabels[l]) + if l+w <= lc.axisXWidth { + lc.labelX = append(lc.labelX, s) + } + l += w + lc.axisXLebelGap + } else { // braille + if 2*l >= len(lc.DataLabels) { + break + } + + s := str2runes(lc.DataLabels[2*l]) + w := strWidth(lc.DataLabels[2*l]) + if l+w <= lc.axisXWidth { + lc.labelX = append(lc.labelX, s) + } + l += w + lc.axisXLebelGap + + } + } +} + +func shortenFloatVal(x float64) string { + s := fmt.Sprintf("%.2f", x) + if len(s)-3 > 3 { + s = fmt.Sprintf("%.2e", x) + } + + if x < 0 { + s = fmt.Sprintf("%.2f", x) + } + return s +} + +func (lc *LineChart) calcLabelY() { + span := lc.topValue - lc.bottomValue + lc.scale = span / float64(lc.axisYHeight) + + n := (1 + lc.axisYHeight) / (lc.axisYLebelGap + 1) + lc.labelY = make([][]rune, n) + maxLen := 0 + for i := 0; i < n; i++ { + s := str2runes(shortenFloatVal(lc.bottomValue + float64(i)*span/float64(n))) + if len(s) > maxLen { + maxLen = len(s) + } + lc.labelY[i] = s + } + + lc.labelYSpace = maxLen +} + +func (lc *LineChart) calcLayout() { + // set datalabels if it is not provided + if lc.DataLabels == nil || len(lc.DataLabels) == 0 { + lc.DataLabels = make([]string, len(lc.Data)) + for i := range lc.Data { + lc.DataLabels[i] = fmt.Sprint(i) + } + } + + // lazy increase, to avoid y shaking frequently + // update bound Y when drawing is gonna overflow + lc.minY = lc.Data[0] + lc.maxY = lc.Data[0] + + // valid visible range + vrange := lc.innerWidth + if lc.Mode == "braille" { + vrange = 2 * lc.innerWidth + } + if vrange > len(lc.Data) { + vrange = len(lc.Data) + } + + for _, v := range lc.Data[:vrange] { + if v > lc.maxY { + lc.maxY = v + } + if v < lc.minY { + lc.minY = v + } + } + + span := lc.maxY - lc.minY + + if lc.minY < lc.bottomValue { + lc.bottomValue = lc.minY - 0.2*span + } + + if lc.maxY > lc.topValue { + lc.topValue = lc.maxY + 0.2*span + } + + lc.axisYHeight = lc.innerHeight - 2 + lc.calcLabelY() + + lc.axisXWidth = lc.innerWidth - 1 - lc.labelYSpace + lc.calcLabelX() + + lc.drawingX = lc.innerX + 1 + lc.labelYSpace + lc.drawingY = lc.innerY +} + +func (lc *LineChart) plotAxes() []Point { + origY := lc.innerY + lc.innerHeight - 2 + origX := lc.innerX + lc.labelYSpace + + ps := []Point{newPointWithAttrs(ORIGIN, origX, origY, lc.AxesColor, lc.BgColor)} + + for x := origX + 1; x < origX+lc.axisXWidth; x++ { + p := Point{} + p.X = x + p.Y = origY + p.Bg = lc.BgColor + p.Fg = lc.AxesColor + p.Ch = HDASH + ps = append(ps, p) + } + + for dy := 1; dy <= lc.axisYHeight; dy++ { + p := Point{} + p.X = origX + p.Y = origY - dy + p.Bg = lc.BgColor + p.Fg = lc.AxesColor + p.Ch = VDASH + ps = append(ps, p) + } + + // x label + oft := 0 + for _, rs := range lc.labelX { + if oft+len(rs) > lc.axisXWidth { + break + } + for j, r := range rs { + p := Point{} + p.Ch = r + p.Fg = lc.AxesColor + p.Bg = lc.BgColor + p.X = origX + oft + j + p.Y = lc.innerY + lc.innerHeight - 1 + ps = append(ps, p) + } + oft += len(rs) + lc.axisXLebelGap + } + + // y labels + for i, rs := range lc.labelY { + for j, r := range rs { + p := Point{} + p.Ch = r + p.Fg = lc.AxesColor + p.Bg = lc.BgColor + p.X = lc.innerX + j + p.Y = origY - i*(lc.axisYLebelGap+1) + ps = append(ps, p) + } + } + + return ps +} + +// Buffer implements Bufferer interface. +func (lc *LineChart) Buffer() []Point { + ps := lc.Block.Buffer() + if lc.Data == nil || len(lc.Data) == 0 { + return ps + } + lc.calcLayout() + ps = append(ps, lc.plotAxes()...) + + if lc.Mode == "dot" { + ps = append(ps, lc.renderDot()...) + } else { + ps = append(ps, lc.renderBraille()...) + } + + return lc.Block.chopOverflow(ps) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go b/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go new file mode 100644 index 000000000..8911873b6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go @@ -0,0 +1,11 @@ +// 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. + +// +build !windows + +package termui + +const VDASH = '┊' +const HDASH = '┈' +const ORIGIN = '└' diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go b/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go new file mode 100644 index 000000000..9f9a5e96c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go @@ -0,0 +1,11 @@ +// 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. + +// +build windows + +package termui + +const VDASH = '|' +const HDASH = '-' +const ORIGIN = '+' diff --git a/Godeps/_workspace/src/github.com/gizak/termui/doc.go b/Godeps/_workspace/src/github.com/gizak/termui/doc.go new file mode 100644 index 000000000..43f886f55 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/doc.go @@ -0,0 +1,27 @@ +// 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 is a library designed for creating command line UI. For more info, goto http://github.com/gizak/termui + +A simplest example: + package main + + import ui "github.com/gizak/termui" + + func main() { + if err:=ui.Init(); err != nil { + panic(err) + } + defer ui.Close() + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.Border.Label = "Gauge" + + ui.Render(g) + } +*/ +package termui diff --git a/Godeps/_workspace/src/github.com/gizak/termui/events.go b/Godeps/_workspace/src/github.com/gizak/termui/events.go new file mode 100644 index 000000000..23a189b56 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/events.go @@ -0,0 +1,219 @@ +// 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. +// +// Portions of this file uses [termbox-go](https://github.com/nsf/termbox-go/blob/54b74d087b7c397c402d0e3b66d2ccb6eaf5c2b4/api_common.go) +// by [authors](https://github.com/nsf/termbox-go/blob/master/AUTHORS) +// under [license](https://github.com/nsf/termbox-go/blob/master/LICENSE) + +package termui + +import "github.com/nsf/termbox-go" + +/***********************************termbox-go**************************************/ + +type ( + EventType uint8 + Modifier uint8 + Key uint16 +) + +// This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are +// valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if +// 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError. +type Event struct { + Type EventType // one of Event* constants + Mod Modifier // one of Mod* constants or 0 + Key Key // one of Key* constants, invalid if 'Ch' is not 0 + Ch rune // a unicode character + Width int // width of the screen + Height int // height of the screen + Err error // error in case if input failed + MouseX int // x coord of mouse + MouseY int // y coord of mouse + N int // number of bytes written when getting a raw event +} + +const ( + KeyF1 Key = 0xFFFF - iota + KeyF2 + KeyF3 + KeyF4 + KeyF5 + KeyF6 + KeyF7 + KeyF8 + KeyF9 + KeyF10 + KeyF11 + KeyF12 + KeyInsert + KeyDelete + KeyHome + KeyEnd + KeyPgup + KeyPgdn + KeyArrowUp + KeyArrowDown + KeyArrowLeft + KeyArrowRight + key_min // see terminfo + MouseLeft + MouseMiddle + MouseRight +) + +const ( + KeyCtrlTilde Key = 0x00 + KeyCtrl2 Key = 0x00 + KeyCtrlSpace Key = 0x00 + KeyCtrlA Key = 0x01 + KeyCtrlB Key = 0x02 + KeyCtrlC Key = 0x03 + KeyCtrlD Key = 0x04 + KeyCtrlE Key = 0x05 + KeyCtrlF Key = 0x06 + KeyCtrlG Key = 0x07 + KeyBackspace Key = 0x08 + KeyCtrlH Key = 0x08 + KeyTab Key = 0x09 + KeyCtrlI Key = 0x09 + KeyCtrlJ Key = 0x0A + KeyCtrlK Key = 0x0B + KeyCtrlL Key = 0x0C + KeyEnter Key = 0x0D + KeyCtrlM Key = 0x0D + KeyCtrlN Key = 0x0E + KeyCtrlO Key = 0x0F + KeyCtrlP Key = 0x10 + KeyCtrlQ Key = 0x11 + KeyCtrlR Key = 0x12 + KeyCtrlS Key = 0x13 + KeyCtrlT Key = 0x14 + KeyCtrlU Key = 0x15 + KeyCtrlV Key = 0x16 + KeyCtrlW Key = 0x17 + KeyCtrlX Key = 0x18 + KeyCtrlY Key = 0x19 + KeyCtrlZ Key = 0x1A + KeyEsc Key = 0x1B + KeyCtrlLsqBracket Key = 0x1B + KeyCtrl3 Key = 0x1B + KeyCtrl4 Key = 0x1C + KeyCtrlBackslash Key = 0x1C + KeyCtrl5 Key = 0x1D + KeyCtrlRsqBracket Key = 0x1D + KeyCtrl6 Key = 0x1E + KeyCtrl7 Key = 0x1F + KeyCtrlSlash Key = 0x1F + KeyCtrlUnderscore Key = 0x1F + KeySpace Key = 0x20 + KeyBackspace2 Key = 0x7F + KeyCtrl8 Key = 0x7F +) + +// Alt modifier constant, see Event.Mod field and SetInputMode function. +const ( + ModAlt Modifier = 0x01 +) + +// Event type. See Event.Type field. +const ( + EventKey EventType = iota + EventResize + EventMouse + EventError + EventInterrupt + EventRaw + EventNone +) + +/**************************************end**************************************/ + +// convert termbox.Event to termui.Event +func uiEvt(e termbox.Event) Event { + event := Event{} + event.Type = EventType(e.Type) + event.Mod = Modifier(e.Mod) + event.Key = Key(e.Key) + event.Ch = e.Ch + event.Width = e.Width + event.Height = e.Height + event.Err = e.Err + event.MouseX = e.MouseX + event.MouseY = e.MouseY + event.N = e.N + + return event +} + +var evtChs = make([]chan Event, 0) + +// EventCh returns an output-only event channel. +// This function can be called many times (multiplexer). +func EventCh() <-chan Event { + out := make(chan Event) + evtChs = append(evtChs, out) + return out +} + +// turn on event listener +func evtListen() { + go func() { + for { + e := termbox.PollEvent() + // dispatch + for _, c := range evtChs { + go func(ch chan Event) { + ch <- uiEvt(e) + }(c) + } + } + }() +} + +/* +// EventHandlers is a handler sequence +var EventHandlers []func(Event) + +var signalQuit = make(chan bool) + +// Quit sends quit signal to terminate termui +func Quit() { + signalQuit <- true +} + +// Wait listening to signalQuit, block operation. +func Wait() { + <-signalQuit +} + +// RegEvtHandler register function into TSEventHandler sequence. +func RegEvtHandler(fn func(Event)) { + EventHandlers = append(EventHandlers, fn) +} + +// EventLoop handles all events and +// redirects every event to callbacks in EventHandlers +func EventLoop() { + evt := make(chan termbox.Event) + + go func() { + for { + evt <- termbox.PollEvent() + } + }() + + for { + select { + case c := <-signalQuit: + defer func() { signalQuit <- c }() + return + case e := <-evt: + for _, fn := range EventHandlers { + fn(uiEvt(e)) + } + } + } +} +*/ diff --git a/Godeps/_workspace/src/github.com/gizak/termui/events_test.go b/Godeps/_workspace/src/github.com/gizak/termui/events_test.go new file mode 100644 index 000000000..1137b1d26 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/events_test.go @@ -0,0 +1,28 @@ +// 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. +// +// Portions of this file uses [termbox-go](https://github.com/nsf/termbox-go/blob/54b74d087b7c397c402d0e3b66d2ccb6eaf5c2b4/api_common.go) +// by [authors](https://github.com/nsf/termbox-go/blob/master/AUTHORS) +// under [license](https://github.com/nsf/termbox-go/blob/master/LICENSE) + +package termui + +import ( + "errors" + "testing" + + termbox "github.com/nsf/termbox-go" + "github.com/stretchr/testify/assert" +) + +type boxEvent termbox.Event + +func TestUiEvt(t *testing.T) { + err := errors.New("This is a mock error") + event := boxEvent{3, 5, 2, 'H', 200, 500, err, 50, 30, 2} + expetced := Event{3, 5, 2, 'H', 200, 500, err, 50, 30, 2} + + // We need to do that ugly casting so that vet does not complain + assert.Equal(t, uiEvt(termbox.Event(event)), expetced) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go new file mode 100644 index 000000000..83947f580 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go @@ -0,0 +1,35 @@ +// 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. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + bc := termui.NewBarChart() + data := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.Border.Label = "Bar Chart" + bc.Data = data + bc.Width = 26 + bc.Height = 10 + bc.DataLabels = bclabels + bc.TextColor = termui.ColorGreen + bc.BarColor = termui.ColorRed + bc.NumColor = termui.ColorYellow + + termui.Render(bc) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png Binary files differnew file mode 100644 index 000000000..a37912f7f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif Binary files differnew file mode 100644 index 000000000..8e1859c72 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go new file mode 100644 index 000000000..c14bb4413 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go @@ -0,0 +1,148 @@ +// 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. + +// +build ignore + +package main + +import ui "github.com/gizak/termui" +import "math" + +import "time" + +func main() { + err := ui.Init() + if err != nil { + panic(err) + } + defer ui.Close() + + p := ui.NewPar(":PRESS q TO QUIT DEMO") + p.Height = 3 + p.Width = 50 + p.TextFgColor = ui.ColorWhite + p.Border.Label = "Text Box" + p.Border.FgColor = ui.ColorCyan + + strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"} + list := ui.NewList() + list.Items = strs + list.ItemFgColor = ui.ColorYellow + list.Border.Label = "List" + list.Height = 7 + list.Width = 25 + list.Y = 4 + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.Height = 3 + g.Y = 11 + g.Border.Label = "Gauge" + g.BarColor = ui.ColorRed + g.Border.FgColor = ui.ColorWhite + g.Border.LabelFgColor = ui.ColorCyan + + spark := ui.Sparkline{} + spark.Height = 1 + spark.Title = "srv 0:" + spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6, 4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6} + spark.Data = spdata + spark.LineColor = ui.ColorCyan + spark.TitleColor = ui.ColorWhite + + spark1 := ui.Sparkline{} + spark1.Height = 1 + spark1.Title = "srv 1:" + spark1.Data = spdata + spark1.TitleColor = ui.ColorWhite + spark1.LineColor = ui.ColorRed + + sp := ui.NewSparklines(spark, spark1) + sp.Width = 25 + sp.Height = 7 + sp.Border.Label = "Sparkline" + sp.Y = 4 + sp.X = 25 + + sinps := (func() []float64 { + n := 220 + ps := make([]float64, n) + for i := range ps { + ps[i] = 1 + math.Sin(float64(i)/5) + } + return ps + })() + + lc := ui.NewLineChart() + lc.Border.Label = "dot-mode Line Chart" + lc.Data = sinps + lc.Width = 50 + lc.Height = 11 + lc.X = 0 + lc.Y = 14 + lc.AxesColor = ui.ColorWhite + lc.LineColor = ui.ColorRed | ui.AttrBold + lc.Mode = "dot" + + bc := ui.NewBarChart() + bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.Border.Label = "Bar Chart" + bc.Width = 26 + bc.Height = 10 + bc.X = 51 + bc.Y = 0 + bc.DataLabels = bclabels + bc.BarColor = ui.ColorGreen + bc.NumColor = ui.ColorBlack + + lc1 := ui.NewLineChart() + lc1.Border.Label = "braille-mode Line Chart" + lc1.Data = sinps + lc1.Width = 26 + lc1.Height = 11 + lc1.X = 51 + lc1.Y = 14 + lc1.AxesColor = ui.ColorWhite + lc1.LineColor = ui.ColorYellow | ui.AttrBold + + p1 := ui.NewPar("Hey!\nI am a borderless block!") + p1.HasBorder = false + p1.Width = 26 + p1.Height = 2 + p1.TextFgColor = ui.ColorMagenta + p1.X = 52 + p1.Y = 11 + + draw := func(t int) { + g.Percent = t % 101 + list.Items = strs[t%9:] + sp.Lines[0].Data = spdata[:30+t%50] + sp.Lines[1].Data = spdata[:35+t%50] + lc.Data = sinps[t/2:] + lc1.Data = sinps[2*t:] + bc.Data = bcdata[t/2%10:] + ui.Render(p, list, g, sp, lc, bc, lc1, p1) + } + + evt := ui.EventCh() + + i := 0 + for { + select { + case e := <-evt: + if e.Type == ui.EventKey && e.Ch == 'q' { + return + } + default: + draw(i) + i++ + if i == 102 { + return + } + time.Sleep(time.Second / 2) + } + } +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go new file mode 100644 index 000000000..b7033580f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go @@ -0,0 +1,62 @@ +// 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. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + g0 := termui.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 + + g2 := termui.NewGauge() + g2.Percent = 60 + g2.Width = 50 + g2.Height = 3 + g2.PercentColor = termui.ColorBlue + g2.Y = 3 + g2.Border.Label = "Slim Gauge" + g2.BarColor = termui.ColorYellow + g2.Border.FgColor = termui.ColorWhite + + g1 := termui.NewGauge() + g1.Percent = 30 + g1.Width = 50 + g1.Height = 5 + g1.Y = 6 + g1.Border.Label = "Big Gauge" + g1.PercentColor = termui.ColorYellow + g1.BarColor = termui.ColorGreen + g1.Border.FgColor = termui.ColorWhite + g1.Border.LabelFgColor = termui.ColorMagenta + + g3 := termui.NewGauge() + g3.Percent = 50 + g3.Width = 50 + g3.Height = 3 + g3.Y = 11 + g3.Border.Label = "Gauge with custom label" + g3.Label = "{{percent}}% (100MBs free)" + g3.LabelAlign = termui.AlignRight + + termui.Render(g0, g1, g2, g3) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png Binary files differnew file mode 100644 index 000000000..5c20e6e8a --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif Binary files differnew file mode 100644 index 000000000..7490043de --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go new file mode 100644 index 000000000..49121411f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go @@ -0,0 +1,134 @@ +// 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. + +// +build ignore + +package main + +import ui "github.com/gizak/termui" +import "math" +import "time" + +func main() { + err := ui.Init() + if err != nil { + panic(err) + } + defer ui.Close() + + sinps := (func() []float64 { + n := 400 + ps := make([]float64, n) + for i := range ps { + ps[i] = 1 + math.Sin(float64(i)/5) + } + return ps + })() + sinpsint := (func() []int { + ps := make([]int, len(sinps)) + for i, v := range sinps { + ps[i] = int(100*v + 10) + } + return ps + })() + + ui.UseTheme("helloworld") + + spark := ui.Sparkline{} + spark.Height = 8 + spdata := sinpsint + spark.Data = spdata[:100] + spark.LineColor = ui.ColorCyan + spark.TitleColor = ui.ColorWhite + + sp := ui.NewSparklines(spark) + sp.Height = 11 + sp.Border.Label = "Sparkline" + + lc := ui.NewLineChart() + lc.Border.Label = "braille-mode Line Chart" + lc.Data = sinps + lc.Height = 11 + lc.AxesColor = ui.ColorWhite + lc.LineColor = ui.ColorYellow | ui.AttrBold + + gs := make([]*ui.Gauge, 3) + for i := range gs { + gs[i] = ui.NewGauge() + gs[i].Height = 2 + gs[i].HasBorder = false + gs[i].Percent = i * 10 + gs[i].PaddingBottom = 1 + gs[i].BarColor = ui.ColorRed + } + + ls := ui.NewList() + ls.HasBorder = false + ls.Items = []string{ + "[1] Downloading File 1", + "", // == \newline + "[2] Downloading File 2", + "", + "[3] Uploading File 3", + } + ls.Height = 5 + + par := ui.NewPar("<> This row has 3 columns\n<- Widgets can be stacked up like left side\n<- Stacked widgets are treated as a single widget") + par.Height = 5 + par.Border.Label = "Demonstration" + + // build layout + ui.Body.AddRows( + ui.NewRow( + ui.NewCol(6, 0, sp), + ui.NewCol(6, 0, lc)), + ui.NewRow( + ui.NewCol(3, 0, ls), + ui.NewCol(3, 0, gs[0], gs[1], gs[2]), + ui.NewCol(6, 0, par))) + + // calculate layout + ui.Body.Align() + + done := make(chan bool) + redraw := make(chan bool) + + update := func() { + for i := 0; i < 103; i++ { + for _, g := range gs { + g.Percent = (g.Percent + 3) % 100 + } + + sp.Lines[0].Data = spdata[:100+i] + lc.Data = sinps[2*i:] + + time.Sleep(time.Second / 2) + redraw <- true + } + done <- true + } + + evt := ui.EventCh() + + ui.Render(ui.Body) + go update() + + for { + select { + case e := <-evt: + if e.Type == ui.EventKey && e.Ch == 'q' { + return + } + if e.Type == ui.EventResize { + ui.Body.Width = ui.TermWidth() + ui.Body.Align() + go func() { redraw <- true }() + } + case <-done: + return + case <-redraw: + ui.Render(ui.Body) + } + } +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go new file mode 100644 index 000000000..1db543496 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go @@ -0,0 +1,68 @@ +// 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. + +// +build ignore + +package main + +import ( + "math" + + "github.com/gizak/termui" +) + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + sinps := (func() []float64 { + n := 220 + ps := make([]float64, n) + for i := range ps { + ps[i] = 1 + math.Sin(float64(i)/5) + } + return ps + })() + + lc0 := termui.NewLineChart() + lc0.Border.Label = "braille-mode Line Chart" + lc0.Data = sinps + lc0.Width = 50 + lc0.Height = 12 + lc0.X = 0 + lc0.Y = 0 + lc0.AxesColor = termui.ColorWhite + lc0.LineColor = termui.ColorGreen | termui.AttrBold + + lc1 := termui.NewLineChart() + lc1.Border.Label = "dot-mode Line Chart" + lc1.Mode = "dot" + lc1.Data = sinps + lc1.Width = 26 + lc1.Height = 12 + lc1.X = 51 + lc1.DotStyle = '+' + lc1.AxesColor = termui.ColorWhite + lc1.LineColor = termui.ColorYellow | termui.AttrBold + + lc2 := termui.NewLineChart() + lc2.Border.Label = "dot-mode Line Chart" + lc2.Mode = "dot" + lc2.Data = sinps[4:] + lc2.Width = 77 + lc2.Height = 16 + lc2.X = 0 + lc2.Y = 12 + lc2.AxesColor = termui.ColorWhite + lc2.LineColor = termui.ColorCyan | termui.AttrBold + + termui.Render(lc0, lc1, lc2) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png Binary files differnew file mode 100644 index 000000000..655ef435f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/list.go b/Godeps/_workspace/src/github.com/gizak/termui/example/list.go new file mode 100644 index 000000000..d33a3616c --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/list.go @@ -0,0 +1,41 @@ +// 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. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + strs := []string{ + "[0] github.com/gizak/termui", + "[1] 你好,世界", + "[2] こんにちは世界", + "[3] keyboard.go", + "[4] output.go", + "[5] random_out.go", + "[6] dashboard.go", + "[7] nsf/termbox-go"} + + ls := termui.NewList() + ls.Items = strs + ls.ItemFgColor = termui.ColorYellow + ls.Border.Label = "List" + ls.Height = 7 + ls.Width = 25 + ls.Y = 0 + + termui.Render(ls) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/list.png b/Godeps/_workspace/src/github.com/gizak/termui/example/list.png Binary files differnew file mode 100644 index 000000000..8ca08c079 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/list.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go new file mode 100644 index 000000000..a32a28e0a --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go @@ -0,0 +1,50 @@ +// 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. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + bc := termui.NewMBarChart() + math := []int{90, 85, 90, 80} + english := []int{70, 85, 75, 60} + science := []int{75, 60, 80, 85} + compsci := []int{100, 100, 100, 100} + bc.Data[0] = math + bc.Data[1] = english + bc.Data[2] = science + bc.Data[3] = compsci + studentsName := []string{"Ken", "Rob", "Dennis", "Linus"} + bc.Border.Label = "Student's Marks X-Axis=Name Y-Axis=Marks[Math,English,Science,ComputerScience] in %" + bc.Width = 100 + bc.Height = 50 + bc.Y = 10 + bc.BarWidth = 10 + bc.DataLabels = studentsName + bc.ShowScale = true //Show y_axis scale value (min and max) + bc.SetMax(400) + + bc.TextColor = termui.ColorGreen //this is color for label (x-axis) + bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience + bc.BarColor[1] = termui.ColorYellow //Bar Color for english + bc.NumColor[3] = termui.ColorRed // Num color for computerscience + bc.NumColor[1] = termui.ColorRed // num color for english + + //Other colors are automatically populated, btw All the students seems do well in computerscience. :p + + termui.Render(bc) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png Binary files differnew file mode 100644 index 000000000..9a4252616 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/par.go b/Godeps/_workspace/src/github.com/gizak/termui/example/par.go new file mode 100644 index 000000000..ffbc60aa8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/par.go @@ -0,0 +1,48 @@ +// 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. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + par0 := termui.NewPar("Borderless Text") + par0.Height = 1 + par0.Width = 20 + par0.Y = 1 + par0.HasBorder = false + + par1 := termui.NewPar("你好,世界。") + par1.Height = 3 + par1.Width = 17 + par1.X = 20 + par1.Border.Label = "标签" + + par2 := termui.NewPar("Simple text\nwith label. It can be multilined with \\n or break automatically") + par2.Height = 5 + par2.Width = 37 + par2.Y = 4 + par2.Border.Label = "Multiline" + par2.Border.FgColor = termui.ColorYellow + + par3 := termui.NewPar("Long text with label and it is auto trimmed.") + par3.Height = 3 + par3.Width = 37 + par3.Y = 9 + par3.Border.Label = "Auto Trim" + + termui.Render(par0, par1, par2, par3) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/par.png b/Godeps/_workspace/src/github.com/gizak/termui/example/par.png Binary files differnew file mode 100644 index 000000000..a85e64415 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/par.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go new file mode 100644 index 000000000..f04baf570 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go @@ -0,0 +1,65 @@ +// 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. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6} + spl0 := termui.NewSparkline() + spl0.Data = data[3:] + spl0.Title = "Sparkline 0" + spl0.LineColor = termui.ColorGreen + + // single + spls0 := termui.NewSparklines(spl0) + spls0.Height = 2 + spls0.Width = 20 + spls0.HasBorder = false + + spl1 := termui.NewSparkline() + spl1.Data = data + spl1.Title = "Sparkline 1" + spl1.LineColor = termui.ColorRed + + spl2 := termui.NewSparkline() + spl2.Data = data[5:] + spl2.Title = "Sparkline 2" + spl2.LineColor = termui.ColorMagenta + + // group + spls1 := termui.NewSparklines(spl0, spl1, spl2) + spls1.Height = 8 + spls1.Width = 20 + spls1.Y = 3 + spls1.Border.Label = "Group Sparklines" + + spl3 := termui.NewSparkline() + spl3.Data = data + spl3.Title = "Enlarged Sparkline" + spl3.Height = 8 + spl3.LineColor = termui.ColorYellow + + spls2 := termui.NewSparklines(spl3) + spls2.Height = 11 + spls2.Width = 30 + spls2.Border.FgColor = termui.ColorCyan + spls2.X = 21 + spls2.Border.Label = "Tweeked Sparkline" + + termui.Render(spls0, spls1, spls2) + + <-termui.EventCh() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png Binary files differnew file mode 100644 index 000000000..9dd7c82b1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go b/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go new file mode 100644 index 000000000..30c51a343 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go @@ -0,0 +1,143 @@ +// 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. + +// +build ignore + +package main + +import ui "github.com/gizak/termui" +import "math" + +import "time" + +func main() { + err := ui.Init() + if err != nil { + panic(err) + } + defer ui.Close() + + ui.UseTheme("helloworld") + + p := ui.NewPar(":PRESS q TO QUIT DEMO") + p.Height = 3 + p.Width = 50 + p.Border.Label = "Text Box" + + strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"} + list := ui.NewList() + list.Items = strs + list.Border.Label = "List" + list.Height = 7 + list.Width = 25 + list.Y = 4 + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.Height = 3 + g.Y = 11 + g.Border.Label = "Gauge" + + spark := ui.NewSparkline() + spark.Title = "srv 0:" + spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6} + spark.Data = spdata + + spark1 := ui.NewSparkline() + spark1.Title = "srv 1:" + spark1.Data = spdata + + sp := ui.NewSparklines(spark, spark1) + sp.Width = 25 + sp.Height = 7 + sp.Border.Label = "Sparkline" + sp.Y = 4 + sp.X = 25 + + lc := ui.NewLineChart() + sinps := (func() []float64 { + n := 100 + ps := make([]float64, n) + for i := range ps { + ps[i] = 1 + math.Sin(float64(i)/4) + } + return ps + })() + + lc.Border.Label = "Line Chart" + lc.Data = sinps + lc.Width = 50 + lc.Height = 11 + lc.X = 0 + lc.Y = 14 + lc.Mode = "dot" + + bc := ui.NewBarChart() + bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.Border.Label = "Bar Chart" + bc.Width = 26 + bc.Height = 10 + bc.X = 51 + bc.Y = 0 + bc.DataLabels = bclabels + + lc1 := ui.NewLineChart() + lc1.Border.Label = "Line Chart" + rndwalk := (func() []float64 { + n := 150 + d := make([]float64, n) + for i := 1; i < n; i++ { + if i < 20 { + d[i] = d[i-1] + 0.01 + } + if i > 20 { + d[i] = d[i-1] - 0.05 + } + } + return d + })() + lc1.Data = rndwalk + lc1.Width = 26 + lc1.Height = 11 + lc1.X = 51 + lc1.Y = 14 + + p1 := ui.NewPar("Hey!\nI am a borderless block!") + p1.HasBorder = false + p1.Width = 26 + p1.Height = 2 + p1.X = 52 + p1.Y = 11 + + draw := func(t int) { + g.Percent = t % 101 + list.Items = strs[t%9:] + sp.Lines[0].Data = spdata[t%10:] + sp.Lines[1].Data = spdata[t/2%10:] + lc.Data = sinps[t/2:] + lc1.Data = rndwalk[t:] + bc.Data = bcdata[t/2%10:] + ui.Render(p, list, g, sp, lc, bc, lc1, p1) + } + + evt := ui.EventCh() + i := 0 + for { + select { + case e := <-evt: + if e.Type == ui.EventKey && e.Ch == 'q' { + return + } + default: + draw(i) + i++ + if i == 102 { + return + } + time.Sleep(time.Second / 2) + } + } +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png Binary files differnew file mode 100644 index 000000000..23b574f96 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png Binary files differnew file mode 100644 index 000000000..eaf4d9303 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png diff --git a/Godeps/_workspace/src/github.com/gizak/termui/gauge.go b/Godeps/_workspace/src/github.com/gizak/termui/gauge.go new file mode 100644 index 000000000..986f4f3dc --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/gauge.go @@ -0,0 +1,113 @@ +// 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" + "strings" +) + +// 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 +*/ + +// Align is the position of the gauge's label. +type Align int + +// All supported positions. +const ( + AlignLeft Align = iota + AlignCenter + AlignRight +) + +type Gauge struct { + Block + Percent int + BarColor Attribute + PercentColor Attribute + Label string + LabelAlign Align +} + +// NewGauge return a new gauge with current theme. +func NewGauge() *Gauge { + g := &Gauge{ + Block: *NewBlock(), + PercentColor: theme.GaugePercent, + BarColor: theme.GaugeBar, + Label: "{{percent}}%", + LabelAlign: AlignCenter, + } + + g.Width = 12 + g.Height = 5 + return g +} + +// Buffer implements Bufferer interface. +func (g *Gauge) Buffer() []Point { + ps := g.Block.Buffer() + + // plot bar + w := g.Percent * g.innerWidth / 100 + 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 + s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1) + pry := g.innerY + g.innerHeight/2 + rs := str2runes(s) + var pos int + switch g.LabelAlign { + case AlignLeft: + pos = 0 + + case AlignCenter: + pos = (g.innerWidth - strWidth(s)) / 2 + + case AlignRight: + pos = g.innerWidth - strWidth(s) + } + + for i, v := range rs { + p := Point{} + p.X = 1 + pos + i + p.Y = pry + p.Ch = v + p.Fg = g.PercentColor + if w+g.innerX > pos+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/Godeps/_workspace/src/github.com/gizak/termui/grid.go b/Godeps/_workspace/src/github.com/gizak/termui/grid.go new file mode 100644 index 000000000..5f6e85e76 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/grid.go @@ -0,0 +1,279 @@ +// 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 + +// GridBufferer introduces a Bufferer that can be manipulated by Grid. +type GridBufferer interface { + Bufferer + GetHeight() int + SetWidth(int) + SetX(int) + SetY(int) +} + +// Row builds a layout tree +type Row struct { + Cols []*Row //children + Widget GridBufferer // root + X int + Y int + Width int + Height int + Span int + Offset int +} + +// calculate and set the underlying layout tree's x, y, height and width. +func (r *Row) calcLayout() { + r.assignWidth(r.Width) + r.Height = r.solveHeight() + r.assignX(r.X) + r.assignY(r.Y) +} + +// tell if the node is leaf in the tree. +func (r *Row) isLeaf() bool { + return r.Cols == nil || len(r.Cols) == 0 +} + +func (r *Row) isRenderableLeaf() bool { + return r.isLeaf() && r.Widget != nil +} + +// assign widgets' (and their parent rows') width recursively. +func (r *Row) assignWidth(w int) { + r.SetWidth(w) + + accW := 0 // acc span and offset + calcW := make([]int, len(r.Cols)) // calculated width + calcOftX := make([]int, len(r.Cols)) // computated start position of x + + for i, c := range r.Cols { + accW += c.Span + c.Offset + cw := int(float64(c.Span*r.Width) / 12.0) + + if i >= 1 { + calcOftX[i] = calcOftX[i-1] + + calcW[i-1] + + int(float64(r.Cols[i-1].Offset*r.Width)/12.0) + } + + // use up the space if it is the last col + if i == len(r.Cols)-1 && accW == 12 { + cw = r.Width - calcOftX[i] + } + calcW[i] = cw + r.Cols[i].assignWidth(cw) + } +} + +// bottom up calc and set rows' (and their widgets') height, +// return r's total height. +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 +} + +// recursively assign x position for r tree. +func (r *Row) assignX(x int) { + r.SetX(x) + + if !r.isLeaf() { + acc := 0 + for i, c := range r.Cols { + if c.Offset != 0 { + acc += int(float64(c.Offset*r.Width) / 12.0) + } + r.Cols[i].assignX(x + acc) + acc += c.Width + } + } +} + +// recursively assign y position to r. +func (r *Row) assignY(y int) { + r.SetY(y) + + if r.isLeaf() { + return + } + + for i := range r.Cols { + acc := 0 + if r.Widget != nil { + acc = r.Widget.GetHeight() + } + r.Cols[i].assignY(y + acc) + } + +} + +// GetHeight implements GridBufferer interface. +func (r Row) GetHeight() int { + return r.Height +} + +// SetX implements GridBufferer interface. +func (r *Row) SetX(x int) { + r.X = x + if r.Widget != nil { + r.Widget.SetX(x) + } +} + +// SetY implements GridBufferer interface. +func (r *Row) SetY(y int) { + r.Y = y + if r.Widget != nil { + r.Widget.SetY(y) + } +} + +// SetWidth implements GridBufferer interface. +func (r *Row) SetWidth(w int) { + r.Width = w + if r.Widget != nil { + r.Widget.SetWidth(w) + } +} + +// Buffer implements Bufferer interface, +// recursively merge all widgets buffer +func (r *Row) Buffer() []Point { + merged := []Point{} + + if r.isRenderableLeaf() { + return r.Widget.Buffer() + } + + // for those are not leaves but have a renderable widget + if r.Widget != nil { + merged = append(merged, r.Widget.Buffer()...) + } + + // collect buffer from children + if !r.isLeaf() { + for _, c := range r.Cols { + merged = append(merged, c.Buffer()...) + } + } + + return merged +} + +// Grid implements 12 columns system. +// A simple example: +/* + import ui "github.com/gizak/termui" + // init and create widgets... + + // build + ui.Body.AddRows( + ui.NewRow( + ui.NewCol(6, 0, widget0), + ui.NewCol(6, 0, widget1)), + ui.NewRow( + ui.NewCol(3, 0, widget2), + ui.NewCol(3, 0, widget30, widget31, widget32), + ui.NewCol(6, 0, widget4))) + + // calculate layout + ui.Body.Align() + + ui.Render(ui.Body) +*/ +type Grid struct { + Rows []*Row + Width int + X int + Y int + BgColor Attribute +} + +// NewGrid returns *Grid with given rows. +func NewGrid(rows ...*Row) *Grid { + return &Grid{Rows: rows} +} + +// AddRows appends given rows to Grid. +func (g *Grid) AddRows(rs ...*Row) { + g.Rows = append(g.Rows, rs...) +} + +// NewRow creates a new row out of given columns. +func NewRow(cols ...*Row) *Row { + rs := &Row{Span: 12, Cols: cols} + return rs +} + +// NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow. +// Note that if multiple widgets are provided, they will stack up in the col. +func NewCol(span, offset int, widgets ...GridBufferer) *Row { + r := &Row{Span: span, Offset: offset} + + if widgets != nil && len(widgets) == 1 { + wgt := widgets[0] + nw, isRow := wgt.(*Row) + if isRow { + r.Cols = nw.Cols + } else { + r.Widget = wgt + } + return r + } + + r.Cols = []*Row{} + ir := r + for _, w := range widgets { + nr := &Row{Span: 12, Widget: w} + ir.Cols = []*Row{nr} + ir = nr + } + + return r +} + +// Align calculate each rows' layout. +func (g *Grid) Align() { + h := 0 + for _, r := range g.Rows { + r.SetWidth(g.Width) + r.SetX(g.X) + r.SetY(g.Y + h) + r.calcLayout() + h += r.GetHeight() + } +} + +// Buffer implments Bufferer interface. +func (g Grid) Buffer() []Point { + ps := []Point{} + for _, r := range g.Rows { + ps = append(ps, r.Buffer()...) + } + return ps +} + +// Body corresponds to the entire terminal display region. +var Body *Grid diff --git a/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go b/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go new file mode 100644 index 000000000..cdafb2052 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go @@ -0,0 +1,98 @@ +// 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 ( + "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 = NewRow( + NewCol(6, 0, p0), + NewCol(6, 0, + NewRow( + NewCol(6, 0, p1), + NewCol(6, 0, p2, 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/Godeps/_workspace/src/github.com/gizak/termui/helper.go b/Godeps/_workspace/src/github.com/gizak/termui/helper.go new file mode 100644 index 000000000..80d8a0278 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/helper.go @@ -0,0 +1,66 @@ +// 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 tm "github.com/nsf/termbox-go" +import rw "github.com/mattn/go-runewidth" + +/* ---------------Port from termbox-go --------------------- */ + +// Attribute is printable cell's color and style. +type Attribute uint16 + +const ( + ColorDefault Attribute = iota + ColorBlack + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +const NumberofColors = 8 //Have a constant that defines number of colors +const ( + AttrBold Attribute = 1 << (iota + 9) + AttrUnderline + AttrReverse +) + +var ( + dot = "…" + dotw = rw.StringWidth(dot) +) + +/* ----------------------- End ----------------------------- */ + +func toTmAttr(x Attribute) tm.Attribute { + return tm.Attribute(x) +} + +func str2runes(s string) []rune { + return []rune(s) +} + +func trimStr2Runes(s string, w int) []rune { + if w <= 0 { + return []rune{} + } + sw := rw.StringWidth(s) + if sw > w { + return []rune(rw.Truncate(s, w, dot)) + } + return str2runes(s) //[]rune(rw.Truncate(s, w, "")) +} + +func strWidth(s string) int { + return rw.StringWidth(s) +} + +func charWidth(ch rune) int { + return rw.RuneWidth(ch) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go b/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go new file mode 100644 index 000000000..6d1a56130 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go @@ -0,0 +1,58 @@ +// 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 ( + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestStr2Rune(t *testing.T) { + s := "你好,世界." + rs := str2runes(s) + if len(rs) != 6 { + t.Error() + } +} + +func TestWidth(t *testing.T) { + s0 := "つのだ☆HIRO" + s1 := "11111111111" + spew.Dump(s0) + spew.Dump(s1) + // above not align for setting East Asian Ambiguous to wide!! + + if strWidth(s0) != strWidth(s1) { + t.Error("str len failed") + } + + len1 := []rune{'a', '2', '&', '「', 'オ', '。'} //will false: 'ᆵ', 'ᄚ', 'ᄒ' + for _, v := range len1 { + if charWidth(v) != 1 { + t.Error("len1 failed") + } + } + + len2 := []rune{'漢', '字', '한', '자', '你', '好', 'だ', '。', '%', 's', 'E', 'ョ', '、', 'ヲ'} + for _, v := range len2 { + if charWidth(v) != 2 { + t.Error("len2 failed") + } + } +} + +func TestTrim(t *testing.T) { + s := "つのだ☆HIRO" + if string(trimStr2Runes(s, 10)) != "つのだ☆HI"+dot { + t.Error("trim failed") + } + if string(trimStr2Runes(s, 11)) != "つのだ☆HIRO" { + t.Error("avoid tail trim failed") + } + if string(trimStr2Runes(s, 15)) != "つのだ☆HIRO" { + t.Error("avoid trim failed") + } +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/list.go b/Godeps/_workspace/src/github.com/gizak/termui/list.go new file mode 100644 index 000000000..0640932f2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/list.go @@ -0,0 +1,104 @@ +// 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 "strings" + +// List displays []string as its items, +// it has a Overflow option (default is "hidden"), when set to "hidden", +// the item exceeding List's width is truncated, but when set to "wrap", +// the overflowed text breaks into next line. +/* + strs := []string{ + "[0] github.com/gizak/termui", + "[1] editbox.go", + "[2] iterrupt.go", + "[3] keyboard.go", + "[4] output.go", + "[5] random_out.go", + "[6] dashboard.go", + "[7] nsf/termbox-go"} + + ls := termui.NewList() + ls.Items = strs + ls.ItemFgColor = termui.ColorYellow + ls.Border.Label = "List" + ls.Height = 7 + ls.Width = 25 + ls.Y = 0 +*/ +type List struct { + Block + Items []string + Overflow string + ItemFgColor Attribute + ItemBgColor Attribute +} + +// NewList returns a new *List with current theme. +func NewList() *List { + l := &List{Block: *NewBlock()} + l.Overflow = "hidden" + l.ItemFgColor = theme.ListItemFg + l.ItemBgColor = theme.ListItemBg + return l +} + +// Buffer implements Bufferer interface. +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) { + w := charWidth(rs[k]) + if rs[k] == '\n' || j+w > l.innerWidth { + i++ + j = 0 + if rs[k] == '\n' { + k++ + } + continue + } + pi := Point{} + pi.X = l.innerX + j + pi.Y = l.innerY + i + + pi.Ch = rs[k] + pi.Bg = l.ItemBgColor + pi.Fg = 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 { + w := charWidth(vv) + p := Point{} + p.X = l.innerX + j + p.Y = l.innerY + i + + p.Ch = vv + p.Bg = l.ItemBgColor + p.Fg = l.ItemFgColor + + ps = append(ps, p) + j += w + } + } + } + return l.Block.chopOverflow(ps) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/mbar.go b/Godeps/_workspace/src/github.com/gizak/termui/mbar.go new file mode 100644 index 000000000..9d18c2cb4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/mbar.go @@ -0,0 +1,233 @@ +// 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 ( + "fmt" +) + +// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go +// Multi-Colored-BarChart creates multiple bars in a widget: +/* + bc := termui.NewMBarChart() + data := make([][]int, 2) + data[0] := []int{3, 2, 5, 7, 9, 4} + data[1] := []int{7, 8, 5, 3, 1, 6} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.Border.Label = "Bar Chart" + bc.Data = data + bc.Width = 26 + bc.Height = 10 + bc.DataLabels = bclabels + bc.TextColor = termui.ColorGreen + bc.BarColor = termui.ColorRed + bc.NumColor = termui.ColorYellow +*/ +type MBarChart struct { + Block + BarColor [NumberofColors]Attribute + TextColor Attribute + NumColor [NumberofColors]Attribute + Data [NumberofColors][]int + DataLabels []string + BarWidth int + BarGap int + labels [][]rune + dataNum [NumberofColors][][]rune + numBar int + scale float64 + max int + minDataLen int + numStack int + ShowScale bool + maxScale []rune +} + +// NewBarChart returns a new *BarChart with current theme. +func NewMBarChart() *MBarChart { + bc := &MBarChart{Block: *NewBlock()} + bc.BarColor[0] = theme.MBarChartBar + bc.NumColor[0] = theme.MBarChartNum + bc.TextColor = theme.MBarChartText + bc.BarGap = 1 + bc.BarWidth = 3 + return bc +} + +func (bc *MBarChart) layout() { + bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth) + bc.labels = make([][]rune, bc.numBar) + DataLen := 0 + LabelLen := len(bc.DataLabels) + bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length + + // We need to know how many stack/data array data[0] , data[1] are there + for i := 0; i < len(bc.Data); i++ { + if bc.Data[i] == nil { + break + } + DataLen++ + } + bc.numStack = DataLen + + //We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs + + for i := 0; i < DataLen; i++ { + if bc.minDataLen > len(bc.Data[i]) { + bc.minDataLen = len(bc.Data[i]) + } + } + + if LabelLen > bc.minDataLen { + LabelLen = bc.minDataLen + } + + for i := 0; i < LabelLen && i < bc.numBar; i++ { + bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth) + } + + for i := 0; i < bc.numStack; i++ { + bc.dataNum[i] = make([][]rune, len(bc.Data[i])) + //For each stack of bar calcualte the rune + for j := 0; j < LabelLen && i < bc.numBar; j++ { + n := bc.Data[i][j] + s := fmt.Sprint(n) + bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth) + } + //If color is not defined by default then populate a color that is different from the prevous bar + if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault { + if i == 0 { + bc.BarColor[i] = ColorBlack + } else { + bc.BarColor[i] = bc.BarColor[i-1] + 1 + if bc.BarColor[i] > NumberofColors { + bc.BarColor[i] = ColorBlack + } + } + bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility + } + } + + //If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n])) + + if bc.max == 0 { + bc.max = -1 + } + for i := 0; i < bc.minDataLen && i < LabelLen; i++ { + var dsum int + for j := 0; j < bc.numStack; j++ { + dsum += bc.Data[j][i] + } + if dsum > bc.max { + bc.max = dsum + } + } + + //Finally Calculate max sale + if bc.ShowScale { + s := fmt.Sprintf("%d", bc.max) + bc.maxScale = trimStr2Runes(s, len(s)) + bc.scale = float64(bc.max) / float64(bc.innerHeight-2) + } else { + bc.scale = float64(bc.max) / float64(bc.innerHeight-1) + } + +} + +func (bc *MBarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *MBarChart) Buffer() []Point { + ps := bc.Block.Buffer() + bc.layout() + var oftX int + + for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ { + ph := 0 //Previous Height to stack up + oftX = i * (bc.BarWidth + bc.BarGap) + for i1 := 0; i1 < bc.numStack; i1++ { + h := int(float64(bc.Data[i1][i]) / bc.scale) + // plot bars + for j := 0; j < bc.BarWidth; j++ { + for k := 0; k < h; k++ { + p := Point{} + p.Ch = ' ' + p.Bg = bc.BarColor[i1] + if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent! + p.Bg |= AttrReverse + } + p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j + p.Y = bc.innerY + bc.innerHeight - 2 - k - ph + ps = append(ps, p) + } + } + ph += h + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + p := Point{} + p.Ch = bc.labels[i][j] + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + bc.innerHeight - 1 + p.X = bc.innerX + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k + ps = append(ps, p) + k += w + } + // plot num + ph = 0 //re-initialize previous height + for i1 := 0; i1 < bc.numStack; i1++ { + h := int(float64(bc.Data[i1][i]) / bc.scale) + for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ { + p := Point{} + p.Ch = bc.dataNum[i1][i][j] + p.Fg = bc.NumColor[i1] + p.Bg = bc.BarColor[i1] + if bc.BarColor[i1] == ColorDefault { // the same as above + p.Bg |= AttrReverse + } + if h == 0 { + p.Bg = bc.BgColor + } + p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j + p.Y = bc.innerY + bc.innerHeight - 2 - ph + ps = append(ps, p) + } + ph += h + } + } + + if bc.ShowScale { + //Currently bar graph only supprts data range from 0 to MAX + //Plot 0 + p := Point{} + p.Ch = '0' + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + bc.innerHeight - 2 + p.X = bc.X + ps = append(ps, p) + + //Plot the maximum sacle value + for i := 0; i < len(bc.maxScale); i++ { + p := Point{} + p.Ch = bc.maxScale[i] + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + p.X = bc.X + i + ps = append(ps, p) + } + + } + + return bc.Block.chopOverflow(ps) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/p.go b/Godeps/_workspace/src/github.com/gizak/termui/p.go new file mode 100644 index 000000000..e327d7489 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/p.go @@ -0,0 +1,71 @@ +// 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 + +// Par displays a paragraph. +/* + par := termui.NewPar("Simple Text") + par.Height = 3 + par.Width = 17 + par.Border.Label = "Label" +*/ +type Par struct { + Block + Text string + TextFgColor Attribute + TextBgColor Attribute +} + +// NewPar returns a new *Par with given text as its content. +func NewPar(s string) *Par { + return &Par{ + Block: *NewBlock(), + Text: s, + TextFgColor: theme.ParTextFg, + TextBgColor: theme.ParTextBg} +} + +// Buffer implements Bufferer interface. +func (p *Par) Buffer() []Point { + ps := p.Block.Buffer() + + rs := str2runes(p.Text) + i, j, k := 0, 0, 0 + for i < p.innerHeight && k < len(rs) { + // the width of char is about to print + w := charWidth(rs[k]) + + if rs[k] == '\n' || j+w > p.innerWidth { + i++ + j = 0 // set x = 0 + if rs[k] == '\n' { + k++ + } + + if i >= p.innerHeight { + ps = append(ps, newPointWithAttrs('…', + p.innerX+p.innerWidth-1, + p.innerY+p.innerHeight-1, + p.TextFgColor, p.TextBgColor)) + break + } + + continue + } + pi := Point{} + pi.X = p.innerX + j + pi.Y = p.innerY + i + + pi.Ch = rs[k] + pi.Bg = p.TextBgColor + pi.Fg = p.TextFgColor + + ps = append(ps, pi) + + k++ + j += w + } + return p.Block.chopOverflow(ps) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/point.go b/Godeps/_workspace/src/github.com/gizak/termui/point.go new file mode 100644 index 000000000..c381af9a4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/point.go @@ -0,0 +1,28 @@ +// 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 +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/render.go b/Godeps/_workspace/src/github.com/gizak/termui/render.go new file mode 100644 index 000000000..d697d0aea --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/render.go @@ -0,0 +1,60 @@ +// 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 tm "github.com/nsf/termbox-go" + +// Bufferer should be implemented by all renderable components. +type Bufferer interface { + Buffer() []Point +} + +// Init initializes termui library. This function should be called before any others. +// After initialization, the library must be finalized by 'Close' function. +func Init() error { + Body = NewGrid() + Body.X = 0 + Body.Y = 0 + Body.BgColor = theme.BodyBg + defer func() { + w, _ := tm.Size() + Body.Width = w + evtListen() + }() + return tm.Init() +} + +// Close finalizes termui library, +// should be called after successful initialization when termui's functionality isn't required anymore. +func Close() { + tm.Close() +} + +// TermWidth returns the current terminal's width. +func TermWidth() int { + tm.Sync() + w, _ := tm.Size() + return w +} + +// TermHeight returns the current terminal's height. +func TermHeight() int { + tm.Sync() + _, h := tm.Size() + return h +} + +// Render renders all Bufferer in the given order from left to right, +// right could overlap on left ones. +func Render(rs ...Bufferer) { + 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)) + } + } + tm.Flush() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go b/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go new file mode 100644 index 000000000..c63a5857f --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go @@ -0,0 +1,156 @@ +// 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 "math" + +// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃ +/* + data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1} + spl := termui.NewSparkline() + spl.Data = data + spl.Title = "Sparkline 0" + spl.LineColor = termui.ColorGreen +*/ +type Sparkline struct { + Data []int + Height int + Title string + TitleColor Attribute + LineColor Attribute + displayHeight int + scale float32 + max int +} + +// Sparklines is a renderable widget which groups together the given sparklines. +/* + spls := termui.NewSparklines(spl0,spl1,spl2) //... + spls.Height = 2 + spls.Width = 20 +*/ +type Sparklines struct { + Block + Lines []Sparkline + displayLines int + displayWidth int +} + +var sparks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} + +// Add appends a given Sparkline to s *Sparklines. +func (s *Sparklines) Add(sl Sparkline) { + s.Lines = append(s.Lines, sl) +} + +// NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines. +func NewSparkline() Sparkline { + return Sparkline{ + Height: 1, + TitleColor: theme.SparklineTitle, + LineColor: theme.SparklineLine} +} + +// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later. +func NewSparklines(ss ...Sparkline) *Sparklines { + s := &Sparklines{Block: *NewBlock(), Lines: ss} + return s +} + +func (sl *Sparklines) update() { + for i, v := range sl.Lines { + if v.Title == "" { + sl.Lines[i].displayHeight = v.Height + } else { + sl.Lines[i].displayHeight = v.Height + 1 + } + } + sl.displayWidth = sl.innerWidth + + // get how many lines gotta display + h := 0 + sl.displayLines = 0 + for _, v := range sl.Lines { + if h+v.displayHeight <= sl.innerHeight { + sl.displayLines++ + } else { + break + } + h += v.displayHeight + } + + for i := 0; i < sl.displayLines; i++ { + data := sl.Lines[i].Data + + max := math.MinInt32 + for _, v := range data { + if max < v { + max = v + } + } + sl.Lines[i].max = max + sl.Lines[i].scale = float32(8*sl.Lines[i].Height) / float32(max) + } +} + +// Buffer implements Bufferer interface. +func (sl *Sparklines) Buffer() []Point { + ps := sl.Block.Buffer() + sl.update() + + oftY := 0 + for i := 0; i < sl.displayLines; i++ { + l := sl.Lines[i] + data := l.Data + + if len(data) > sl.innerWidth { + data = data[len(data)-sl.innerWidth:] + } + + if l.Title != "" { + rs := trimStr2Runes(l.Title, sl.innerWidth) + oftX := 0 + for _, v := range rs { + w := charWidth(v) + p := Point{} + p.Ch = v + p.Fg = l.TitleColor + p.Bg = sl.BgColor + p.X = sl.innerX + oftX + p.Y = sl.innerY + oftY + ps = append(ps, p) + oftX += w + } + } + + for j, v := range data { + h := int(float32(v)*l.scale + 0.5) + barCnt := h / 8 + barMod := h % 8 + for jj := 0; jj < barCnt; jj++ { + p := Point{} + p.X = sl.innerX + j + p.Y = sl.innerY + oftY + l.Height - jj + p.Ch = ' ' // => sparks[7] + p.Bg = l.LineColor + //p.Bg = sl.BgColor + ps = append(ps, p) + } + if barMod != 0 { + p := Point{} + p.X = sl.innerX + j + p.Y = sl.innerY + oftY + l.Height - barCnt + p.Ch = sparks[barMod-1] + p.Fg = l.LineColor + p.Bg = sl.BgColor + ps = append(ps, p) + } + } + + oftY += l.displayHeight + } + + return sl.Block.chopOverflow(ps) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/theme.go b/Godeps/_workspace/src/github.com/gizak/termui/theme.go new file mode 100644 index 000000000..c8ad94756 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/theme.go @@ -0,0 +1,84 @@ +// 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 + +// A ColorScheme represents the current look-and-feel of the dashboard. +type ColorScheme struct { + BodyBg Attribute + BlockBg Attribute + HasBorder bool + BorderFg Attribute + BorderBg Attribute + BorderLabelTextFg Attribute + BorderLabelTextBg Attribute + ParTextFg Attribute + ParTextBg Attribute + SparklineLine Attribute + SparklineTitle Attribute + GaugeBar Attribute + GaugePercent Attribute + LineChartLine Attribute + LineChartAxes Attribute + ListItemFg Attribute + ListItemBg Attribute + BarChartBar Attribute + BarChartText Attribute + BarChartNum Attribute + MBarChartBar Attribute + MBarChartText Attribute + MBarChartNum Attribute +} + +// default color scheme depends on the user's terminal setting. +var themeDefault = ColorScheme{HasBorder: true} + +var themeHelloWorld = ColorScheme{ + BodyBg: ColorBlack, + BlockBg: ColorBlack, + HasBorder: true, + BorderFg: ColorWhite, + BorderBg: ColorBlack, + BorderLabelTextBg: ColorBlack, + BorderLabelTextFg: ColorGreen, + ParTextBg: ColorBlack, + ParTextFg: ColorWhite, + SparklineLine: ColorMagenta, + SparklineTitle: ColorWhite, + GaugeBar: ColorRed, + GaugePercent: ColorWhite, + LineChartLine: ColorYellow | AttrBold, + LineChartAxes: ColorWhite, + ListItemBg: ColorBlack, + ListItemFg: ColorYellow, + BarChartBar: ColorRed, + BarChartNum: ColorWhite, + BarChartText: ColorCyan, + MBarChartBar: ColorRed, + MBarChartNum: ColorWhite, + MBarChartText: ColorCyan, +} + +var theme = themeDefault // global dep + +// Theme returns the currently used theme. +func Theme() ColorScheme { + return theme +} + +// SetTheme sets a new, custom theme. +func SetTheme(newTheme ColorScheme) { + theme = newTheme +} + +// UseTheme sets a predefined scheme. Currently available: "hello-world" and +// "black-and-white". +func UseTheme(th string) { + switch th { + case "helloworld": + theme = themeHelloWorld + default: + theme = themeDefault + } +} |