aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/gizak/termui
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2015-06-25 23:18:42 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2015-06-25 23:18:42 +0800
commitb0a5be4495962c291a25cbea793e43bad0781510 (patch)
treee058201e7b29c3cb8efd710e20ec1e1f97f64368 /Godeps/_workspace/src/github.com/gizak/termui
parente64625aa8215985c85f97f914a98db081e07714f (diff)
parente9c0b5431cbd7430ddec9fd17983241018fd8a55 (diff)
downloadgo-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')
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/.gitignore24
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/.travis.yml6
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/LICENSE22
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/README.md159
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/bar.go135
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/block.go142
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/block_test.go46
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/box.go117
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/box_others.go14
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/box_windows.go14
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/canvas.go74
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go55
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/chart.go336
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/chart_others.go11
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go11
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/doc.go27
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/events.go219
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/events_test.go28
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go35
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/barchart.pngbin0 -> 15386 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gifbin0 -> 453764 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go148
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go62
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/gauge.pngbin0 -> 32431 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/grid.gifbin0 -> 800288 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/grid.go134
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go68
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/linechart.pngbin0 -> 139361 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/list.go41
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/list.pngbin0 -> 37227 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go50
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.pngbin0 -> 20075 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/par.go48
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/par.pngbin0 -> 65773 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go65
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.pngbin0 -> 43431 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/theme.go143
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.pngbin0 -> 105508 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.pngbin0 -> 90111 bytes
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/gauge.go113
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/grid.go279
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/grid_test.go98
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/helper.go66
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/helper_test.go58
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/list.go104
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/mbar.go233
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/p.go71
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/point.go28
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/render.go60
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/sparkline.go156
-rw-r--r--Godeps/_workspace/src/github.com/gizak/termui/theme.go84
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
new file mode 100644
index 000000000..a37912f7f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif
new file mode 100644
index 000000000..8e1859c72
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif
Binary files differ
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
new file mode 100644
index 000000000..5c20e6e8a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif
new file mode 100644
index 000000000..7490043de
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif
Binary files differ
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
new file mode 100644
index 000000000..655ef435f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png
Binary files differ
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
new file mode 100644
index 000000000..8ca08c079
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/list.png
Binary files differ
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
new file mode 100644
index 000000000..9a4252616
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png
Binary files differ
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
new file mode 100644
index 000000000..a85e64415
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/par.png
Binary files differ
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
new file mode 100644
index 000000000..9dd7c82b1
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png
Binary files differ
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
new file mode 100644
index 000000000..23b574f96
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png
Binary files differ
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png
new file mode 100644
index 000000000..eaf4d9303
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png
Binary files differ
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
+ }
+}