diff options
author | Péter Szilágyi <peterke@gmail.com> | 2016-10-29 01:05:01 +0800 |
---|---|---|
committer | Felix Lange <fjl@twurst.com> | 2016-10-29 01:05:01 +0800 |
commit | 289b30715d097edafd5562f66cb3567a70b2d330 (patch) | |
tree | 7eaaa6da97c84727469303b986e364606ece57ce /vendor/github.com/gizak/termui | |
parent | 77703045765343c489ded2f43e3ed0f332c5f148 (diff) | |
download | go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.tar go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.tar.gz go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.tar.bz2 go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.tar.lz go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.tar.xz go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.tar.zst go-tangerine-289b30715d097edafd5562f66cb3567a70b2d330.zip |
Godeps, vendor: convert dependency management to trash (#3198)
This commit converts the dependency management from Godeps to the vendor
folder, also switching the tool from godep to trash. Since the upstream tool
lacks a few features proposed via a few PRs, until those PRs are merged in
(if), use github.com/karalabe/trash.
You can update dependencies via trash --update.
All dependencies have been updated to their latest version.
Parts of the build system are reworked to drop old notions of Godeps and
invocation of the go vet command so that it doesn't run against the vendor
folder, as that will just blow up during vetting.
The conversion drops OpenCL (and hence GPU mining support) from ethash and our
codebase. The short reasoning is that there's noone to maintain and having
opencl libs in our deps messes up builds as go install ./... tries to build
them, failing with unsatisfied link errors for the C OpenCL deps.
golang.org/x/net/context is not vendored in. We expect it to be fetched by the
user (i.e. using go get). To keep ci.go builds reproducible the package is
"vendored" in build/_vendor.
Diffstat (limited to 'vendor/github.com/gizak/termui')
31 files changed, 3492 insertions, 0 deletions
diff --git a/vendor/github.com/gizak/termui/.gitignore b/vendor/github.com/gizak/termui/.gitignore new file mode 100644 index 000000000..8b156b020 --- /dev/null +++ b/vendor/github.com/gizak/termui/.gitignore @@ -0,0 +1,26 @@ +# 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 +.DS_Store +/vendor diff --git a/vendor/github.com/gizak/termui/.travis.yml b/vendor/github.com/gizak/termui/.travis.yml new file mode 100644 index 000000000..206e88740 --- /dev/null +++ b/vendor/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/vendor/github.com/gizak/termui/LICENSE b/vendor/github.com/gizak/termui/LICENSE new file mode 100644 index 000000000..311ccc74f --- /dev/null +++ b/vendor/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/vendor/github.com/gizak/termui/README.md b/vendor/github.com/gizak/termui/README.md new file mode 100644 index 000000000..4f3d4a419 --- /dev/null +++ b/vendor/github.com/gizak/termui/README.md @@ -0,0 +1,150 @@ +# 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) + +<img src="./_example/dashboard.gif" alt="demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)" width="80%"> + +`termui` is a cross-platform, easy-to-compile, and fully-customizable terminal dashboard. It is inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go. + +Now version v2 has arrived! It brings new event system, new theme system, new `Buffer` interface and specific colour text rendering. (some docs are missing, but it will be completed soon!) + +## Installation + +`master` mirrors v2 branch, to install: + + go get -u github.com/gizak/termui + +It is recommanded to use locked deps by using [glide](https://glide.sh): move to `termui` src directory then run `glide up`. + +For the compatible reason, you can choose to install the legacy version of `termui`: + + go get gopkg.in/gizak/termui.v1 + +## Usage + +### Layout + +To use `termui`, the very first thing you may want to know is how to manage layout. `termui` offers two ways of doing this, known as absolute layout and grid layout. + +__Absolute layout__ + +Each widget has an underlying block structure which basically is a box model. It has border, label and padding properties. A border of a widget can be chosen to hide or display (with its border label), you can pick a different front/back colour for the border as well. To display such a widget at a specific location in terminal window, you need to assign `.X`, `.Y`, `.Height`, `.Width` values for each widget before sending it to `.Render`. Let's demonstrate these by a code snippet: + +`````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.BorderLabel = "Text Box" + p.BorderFg = ui.ColorCyan + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.Height = 3 + g.Y = 11 + g.BorderLabel = "Gauge" + g.BarColor = ui.ColorRed + g.BorderFg = ui.ColorWhite + g.BorderLabelFg = ui.ColorCyan + + ui.Render(p, g) // feel free to call Render, it's async and non-block + + // 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). + +__Grid layout:__ + +<img src="./_example/grid.gif" alt="grid" width="60%"> + +Grid layout uses [12 columns grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) with expressive syntax. To use `Grid`, all we need to do is build a widget tree consisting of `Row`s and `Col`s (Actually a `Col` is also a `Row` but with a widget endpoint attached). + +```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) +``` + +### Events + +`termui` ships with a http-like event mux handling system. All events are channeled up from different sources (typing, click, windows resize, custom event) and then encoded as universal `Event` object. `Event.Path` indicates the event type and `Event.Data` stores the event data struct. Add a handler to a certain event is easy as below: + +```go + // handle key q pressing + ui.Handle("/sys/kbd/q", func(ui.Event) { + // press q to quit + ui.StopLoop() + }) + + ui.Handle("/sys/kbd/C-x", func(ui.Event) { + // handle Ctrl + x combination + }) + + ui.Handle("/sys/kbd", func(ui.Event) { + // handle all other key pressing + }) + + // handle a 1s timer + ui.Handle("/timer/1s", func(e ui.Event) { + t := e.Data.(ui.EvtTimer) + // t is a EvtTimer + if t.Count%2 ==0 { + // do something + } + }) + + ui.Loop() // block until StopLoop is called +``` + +### Widgets + +Click image to see the corresponding demo codes. + +[<img src="./_example/par.png" alt="par" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/par.go) +[<img src="./_example/list.png" alt="list" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/list.go) +[<img src="./_example/gauge.png" alt="gauge" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/gauge.go) +[<img src="./_example/linechart.png" alt="linechart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/linechart.go) +[<img src="./_example/barchart.png" alt="barchart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/barchart.go) +[<img src="./_example/mbarchart.png" alt="barchart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/mbarchart.go) +[<img src="./_example/sparklines.png" alt="sparklines" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/sparklines.go) + +## GoDoc + +[godoc](https://godoc.org/github.com/gizak/termui) + +## TODO + +- [x] Grid layout +- [x] Event system +- [x] Canvas widget +- [x] Refine APIs +- [ ] Focusable widgets + +## Changelog + +## License +This library is under the [MIT License](http://opensource.org/licenses/MIT) diff --git a/vendor/github.com/gizak/termui/barchart.go b/vendor/github.com/gizak/termui/barchart.go new file mode 100644 index 000000000..9e2a10689 --- /dev/null +++ b/vendor/github.com/gizak/termui/barchart.go @@ -0,0 +1,149 @@ +// Copyright 2016 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.BorderLabel = "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 + CellChar rune + 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 = ThemeAttr("barchart.bar.bg") + bc.NumColor = ThemeAttr("barchart.num.fg") + bc.TextColor = ThemeAttr("barchart.text.fg") + bc.BarGap = 1 + bc.BarWidth = 3 + bc.CellChar = ' ' + return bc +} + +func (bc *BarChart) layout() { + bc.numBar = bc.innerArea.Dx() / (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.innerArea.Dy()-1) +} + +func (bc *BarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *BarChart) Buffer() Buffer { + buf := 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) + + barBg := bc.Bg + barFg := bc.BarColor + + if bc.CellChar == ' ' { + barBg = bc.BarColor + barFg = ColorDefault + if bc.BarColor == ColorDefault { // the same as above + barBg |= AttrReverse + } + } + + // plot bar + for j := 0; j < bc.BarWidth; j++ { + for k := 0; k < h; k++ { + c := Cell{ + Ch: bc.CellChar, + Bg: barBg, + Fg: barFg, + } + + x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k + buf.Set(x, y, c) + } + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + c := Cell{ + Ch: bc.labels[i][j], + Bg: bc.Bg, + Fg: bc.TextColor, + } + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1 + x := bc.innerArea.Min.X + oftX + k + buf.Set(x, y, c) + k += w + } + // plot num + for j := 0; j < len(bc.dataNum[i]); j++ { + c := Cell{ + Ch: bc.dataNum[i][j], + Fg: bc.NumColor, + Bg: barBg, + } + + if h == 0 { + c.Bg = bc.Bg + } + x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 + buf.Set(x, y, c) + } + } + + return buf +} diff --git a/vendor/github.com/gizak/termui/block.go b/vendor/github.com/gizak/termui/block.go new file mode 100644 index 000000000..418738c8d --- /dev/null +++ b/vendor/github.com/gizak/termui/block.go @@ -0,0 +1,240 @@ +// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Hline is a horizontal line. +type Hline struct { + X int + Y int + Len int + Fg Attribute + Bg Attribute +} + +// Vline is a vertical line. +type Vline struct { + X int + Y int + Len int + Fg Attribute + Bg Attribute +} + +// Buffer draws a horizontal line. +func (l Hline) Buffer() Buffer { + if l.Len <= 0 { + return NewBuffer() + } + return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg) +} + +// Buffer draws a vertical line. +func (l Vline) Buffer() Buffer { + if l.Len <= 0 { + return NewBuffer() + } + return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg) +} + +// Buffer draws a box border. +func (b Block) drawBorder(buf Buffer) { + if !b.Border { + return + } + + min := b.area.Min + max := b.area.Max + + x0 := min.X + y0 := min.Y + x1 := max.X - 1 + y1 := max.Y - 1 + + // draw lines + if b.BorderTop { + buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer()) + } + if b.BorderBottom { + buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer()) + } + if b.BorderLeft { + buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer()) + } + if b.BorderRight { + buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer()) + } + + // draw corners + if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 { + buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg}) + } + if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 { + buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg}) + } + if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 { + buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg}) + } + if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 { + buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg}) + } +} + +func (b Block) drawBorderLabel(buf Buffer) { + maxTxtW := b.area.Dx() - 2 + tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW) + + for i, w := 0, 0; i < len(tx); i++ { + buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i]) + w += tx[i].Width() + } +} + +// Block is a base struct for all other upper level widgets, +// consider it as css: display:block. +// Normally you do not need to create it manually. +type Block struct { + area image.Rectangle + innerArea image.Rectangle + X int + Y int + Border bool + BorderFg Attribute + BorderBg Attribute + BorderLeft bool + BorderRight bool + BorderTop bool + BorderBottom bool + BorderLabel string + BorderLabelFg Attribute + BorderLabelBg Attribute + Display bool + Bg Attribute + Width int + Height int + PaddingTop int + PaddingBottom int + PaddingLeft int + PaddingRight int + id string + Float Align +} + +// NewBlock returns a *Block which inherits styles from current theme. +func NewBlock() *Block { + b := Block{} + b.Display = true + b.Border = true + b.BorderLeft = true + b.BorderRight = true + b.BorderTop = true + b.BorderBottom = true + b.BorderBg = ThemeAttr("border.bg") + b.BorderFg = ThemeAttr("border.fg") + b.BorderLabelBg = ThemeAttr("label.bg") + b.BorderLabelFg = ThemeAttr("label.fg") + b.Bg = ThemeAttr("block.bg") + b.Width = 2 + b.Height = 2 + b.id = GenId() + b.Float = AlignNone + return &b +} + +func (b Block) Id() string { + return b.id +} + +// Align computes box model +func (b *Block) Align() { + // outer + b.area.Min.X = 0 + b.area.Min.Y = 0 + b.area.Max.X = b.Width + b.area.Max.Y = b.Height + + // float + b.area = AlignArea(TermRect(), b.area, b.Float) + b.area = MoveArea(b.area, b.X, b.Y) + + // inner + b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft + b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop + b.innerArea.Max.X = b.area.Max.X - b.PaddingRight + b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom + + if b.Border { + if b.BorderLeft { + b.innerArea.Min.X++ + } + if b.BorderRight { + b.innerArea.Max.X-- + } + if b.BorderTop { + b.innerArea.Min.Y++ + } + if b.BorderBottom { + b.innerArea.Max.Y-- + } + } +} + +// InnerBounds returns the internal bounds of the block after aligning and +// calculating the padding and border, if any. +func (b *Block) InnerBounds() image.Rectangle { + b.Align() + return b.innerArea +} + +// Buffer implements Bufferer interface. +// Draw background and border (if any). +func (b *Block) Buffer() Buffer { + b.Align() + + buf := NewBuffer() + buf.SetArea(b.area) + buf.Fill(' ', ColorDefault, b.Bg) + + b.drawBorder(buf) + b.drawBorderLabel(buf) + + return buf +} + +// GetHeight implements GridBufferer. +// It returns current height of the block. +func (b Block) GetHeight() int { + return b.Height +} + +// SetX implements GridBufferer interface, which sets block's x position. +func (b *Block) SetX(x int) { + b.X = x +} + +// SetY implements GridBufferer interface, it sets y position for block. +func (b *Block) SetY(y int) { + b.Y = y +} + +// SetWidth implements GridBuffer interface, it sets block's width. +func (b *Block) SetWidth(w int) { + b.Width = w +} + +func (b Block) InnerWidth() int { + return b.innerArea.Dx() +} + +func (b Block) InnerHeight() int { + return b.innerArea.Dy() +} + +func (b Block) InnerX() int { + return b.innerArea.Min.X +} + +func (b Block) InnerY() int { return b.innerArea.Min.Y } diff --git a/vendor/github.com/gizak/termui/block_common.go b/vendor/github.com/gizak/termui/block_common.go new file mode 100644 index 000000000..0f972cbbd --- /dev/null +++ b/vendor/github.com/gizak/termui/block_common.go @@ -0,0 +1,20 @@ +// Copyright 2016 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 = '└' +const VERTICAL_LEFT = '┤' +const VERTICAL_RIGHT = '├' +const HORIZONTAL_DOWN = '┬' +const HORIZONTAL_UP = '┴' +const QUOTA_LEFT = '«' +const QUOTA_RIGHT = '»' diff --git a/vendor/github.com/gizak/termui/block_windows.go b/vendor/github.com/gizak/termui/block_windows.go new file mode 100644 index 000000000..4dbc017de --- /dev/null +++ b/vendor/github.com/gizak/termui/block_windows.go @@ -0,0 +1,14 @@ +// Copyright 2016 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/vendor/github.com/gizak/termui/buffer.go b/vendor/github.com/gizak/termui/buffer.go new file mode 100644 index 000000000..60e77863a --- /dev/null +++ b/vendor/github.com/gizak/termui/buffer.go @@ -0,0 +1,106 @@ +// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Cell is a rune with assigned Fg and Bg +type Cell struct { + Ch rune + Fg Attribute + Bg Attribute +} + +// Buffer is a renderable rectangle cell data container. +type Buffer struct { + Area image.Rectangle // selected drawing area + CellMap map[image.Point]Cell +} + +// At returns the cell at (x,y). +func (b Buffer) At(x, y int) Cell { + return b.CellMap[image.Pt(x, y)] +} + +// Set assigns a char to (x,y) +func (b Buffer) Set(x, y int, c Cell) { + b.CellMap[image.Pt(x, y)] = c +} + +// Bounds returns the domain for which At can return non-zero color. +func (b Buffer) Bounds() image.Rectangle { + x0, y0, x1, y1 := 0, 0, 0, 0 + for p := range b.CellMap { + if p.X > x1 { + x1 = p.X + } + if p.X < x0 { + x0 = p.X + } + if p.Y > y1 { + y1 = p.Y + } + if p.Y < y0 { + y0 = p.Y + } + } + return image.Rect(x0, y0, x1, y1) +} + +// SetArea assigns a new rect area to Buffer b. +func (b *Buffer) SetArea(r image.Rectangle) { + b.Area.Max = r.Max + b.Area.Min = r.Min +} + +// Sync sets drawing area to the buffer's bound +func (b Buffer) Sync() { + b.SetArea(b.Bounds()) +} + +// NewCell returns a new cell +func NewCell(ch rune, fg, bg Attribute) Cell { + return Cell{ch, fg, bg} +} + +// Merge merges bs Buffers onto b +func (b *Buffer) Merge(bs ...Buffer) { + for _, buf := range bs { + for p, v := range buf.CellMap { + b.Set(p.X, p.Y, v) + } + b.SetArea(b.Area.Union(buf.Area)) + } +} + +// NewBuffer returns a new Buffer +func NewBuffer() Buffer { + return Buffer{ + CellMap: make(map[image.Point]Cell), + Area: image.Rectangle{}} +} + +// Fill fills the Buffer b with ch,fg and bg. +func (b Buffer) Fill(ch rune, fg, bg Attribute) { + for x := b.Area.Min.X; x < b.Area.Max.X; x++ { + for y := b.Area.Min.Y; y < b.Area.Max.Y; y++ { + b.Set(x, y, Cell{ch, fg, bg}) + } + } +} + +// NewFilledBuffer returns a new Buffer filled with ch, fb and bg. +func NewFilledBuffer(x0, y0, x1, y1 int, ch rune, fg, bg Attribute) Buffer { + buf := NewBuffer() + buf.Area.Min = image.Pt(x0, y0) + buf.Area.Max = image.Pt(x1, y1) + + for x := buf.Area.Min.X; x < buf.Area.Max.X; x++ { + for y := buf.Area.Min.Y; y < buf.Area.Max.Y; y++ { + buf.Set(x, y, Cell{ch, fg, bg}) + } + } + return buf +} diff --git a/vendor/github.com/gizak/termui/canvas.go b/vendor/github.com/gizak/termui/canvas.go new file mode 100644 index 000000000..4173780f3 --- /dev/null +++ b/vendor/github.com/gizak/termui/canvas.go @@ -0,0 +1,72 @@ +// Copyright 2016 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() Buffer { + buf := NewBuffer() + for k, v := range c { + buf.Set(k[0], k[1], Cell{Ch: v + brailleBase}) + } + return buf +} diff --git a/vendor/github.com/gizak/termui/config b/vendor/github.com/gizak/termui/config new file mode 100755 index 000000000..18fd6a401 --- /dev/null +++ b/vendor/github.com/gizak/termui/config @@ -0,0 +1,26 @@ +#!/usr/bin/env perl6 + +use v6; + +my $copyright = '// Copyright 2016 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. + +'; + +sub MAIN('update-docstr', Str $srcp) { + if $srcp.IO.f { + $_ = $srcp.IO.slurp; + if m/^ \/\/\s Copyright .+? \n\n/ { + unless ~$/ eq $copyright { + s/^ \/\/\s Copyright .+? \n\n /$copyright/; + spurt $srcp, $_; + say "[updated] doc string for:"~$srcp; + } + } else { + say "[added] doc string for "~$srcp~" (no match found)"; + $_ = $copyright ~ $_; + spurt $srcp, $_; + } + } +} diff --git a/vendor/github.com/gizak/termui/doc.go b/vendor/github.com/gizak/termui/doc.go new file mode 100644 index 000000000..0d7108d30 --- /dev/null +++ b/vendor/github.com/gizak/termui/doc.go @@ -0,0 +1,29 @@ +// Copyright 2016 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.BorderLabel = "Gauge" + + ui.Render(g) + + ui.Loop() + } +*/ +package termui diff --git a/vendor/github.com/gizak/termui/events.go b/vendor/github.com/gizak/termui/events.go new file mode 100644 index 000000000..6627f8942 --- /dev/null +++ b/vendor/github.com/gizak/termui/events.go @@ -0,0 +1,323 @@ +// Copyright 2016 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 ( + "path" + "strconv" + "sync" + "time" + + "github.com/nsf/termbox-go" +) + +type Event struct { + Type string + Path string + From string + To string + Data interface{} + Time int64 +} + +var sysEvtChs []chan Event + +type EvtKbd struct { + KeyStr string +} + +func evtKbd(e termbox.Event) EvtKbd { + ek := EvtKbd{} + + k := string(e.Ch) + pre := "" + mod := "" + + if e.Mod == termbox.ModAlt { + mod = "M-" + } + if e.Ch == 0 { + if e.Key > 0xFFFF-12 { + k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">" + } else if e.Key > 0xFFFF-25 { + ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"} + k = ks[0xFFFF-int(e.Key)-12] + } + + if e.Key <= 0x7F { + pre = "C-" + k = string('a' - 1 + int(e.Key)) + kmap := map[termbox.Key][2]string{ + termbox.KeyCtrlSpace: {"C-", "<space>"}, + termbox.KeyBackspace: {"", "<backspace>"}, + termbox.KeyTab: {"", "<tab>"}, + termbox.KeyEnter: {"", "<enter>"}, + termbox.KeyEsc: {"", "<escape>"}, + termbox.KeyCtrlBackslash: {"C-", "\\"}, + termbox.KeyCtrlSlash: {"C-", "/"}, + termbox.KeySpace: {"", "<space>"}, + termbox.KeyCtrl8: {"C-", "8"}, + } + if sk, ok := kmap[e.Key]; ok { + pre = sk[0] + k = sk[1] + } + } + } + + ek.KeyStr = pre + mod + k + return ek +} + +func crtTermboxEvt(e termbox.Event) Event { + systypemap := map[termbox.EventType]string{ + termbox.EventKey: "keyboard", + termbox.EventResize: "window", + termbox.EventMouse: "mouse", + termbox.EventError: "error", + termbox.EventInterrupt: "interrupt", + } + ne := Event{From: "/sys", Time: time.Now().Unix()} + typ := e.Type + ne.Type = systypemap[typ] + + switch typ { + case termbox.EventKey: + kbd := evtKbd(e) + ne.Path = "/sys/kbd/" + kbd.KeyStr + ne.Data = kbd + case termbox.EventResize: + wnd := EvtWnd{} + wnd.Width = e.Width + wnd.Height = e.Height + ne.Path = "/sys/wnd/resize" + ne.Data = wnd + case termbox.EventError: + err := EvtErr(e.Err) + ne.Path = "/sys/err" + ne.Data = err + case termbox.EventMouse: + m := EvtMouse{} + m.X = e.MouseX + m.Y = e.MouseY + ne.Path = "/sys/mouse" + ne.Data = m + } + return ne +} + +type EvtWnd struct { + Width int + Height int +} + +type EvtMouse struct { + X int + Y int + Press string +} + +type EvtErr error + +func hookTermboxEvt() { + for { + e := termbox.PollEvent() + + for _, c := range sysEvtChs { + go func(ch chan Event) { + ch <- crtTermboxEvt(e) + }(c) + } + } +} + +func NewSysEvtCh() chan Event { + ec := make(chan Event) + sysEvtChs = append(sysEvtChs, ec) + return ec +} + +var DefaultEvtStream = NewEvtStream() + +type EvtStream struct { + sync.RWMutex + srcMap map[string]chan Event + stream chan Event + wg sync.WaitGroup + sigStopLoop chan Event + Handlers map[string]func(Event) + hook func(Event) +} + +func NewEvtStream() *EvtStream { + return &EvtStream{ + srcMap: make(map[string]chan Event), + stream: make(chan Event), + Handlers: make(map[string]func(Event)), + sigStopLoop: make(chan Event), + } +} + +func (es *EvtStream) Init() { + es.Merge("internal", es.sigStopLoop) + go func() { + es.wg.Wait() + close(es.stream) + }() +} + +func cleanPath(p string) string { + if p == "" { + return "/" + } + if p[0] != '/' { + p = "/" + p + } + return path.Clean(p) +} + +func isPathMatch(pattern, path string) bool { + if len(pattern) == 0 { + return false + } + n := len(pattern) + return len(path) >= n && path[0:n] == pattern +} + +func (es *EvtStream) Merge(name string, ec chan Event) { + es.Lock() + defer es.Unlock() + + es.wg.Add(1) + es.srcMap[name] = ec + + go func(a chan Event) { + for n := range a { + n.From = name + es.stream <- n + } + es.wg.Done() + }(ec) +} + +func (es *EvtStream) Handle(path string, handler func(Event)) { + es.Handlers[cleanPath(path)] = handler +} + +func findMatch(mux map[string]func(Event), path string) string { + n := -1 + pattern := "" + for m := range mux { + if !isPathMatch(m, path) { + continue + } + if len(m) > n { + pattern = m + n = len(m) + } + } + return pattern + +} +// Remove all existing defined Handlers from the map +func (es *EvtStream) ResetHandlers() { + for Path, _ := range es.Handlers { + delete(es.Handlers, Path) + } + return +} + +func (es *EvtStream) match(path string) string { + return findMatch(es.Handlers, path) +} + +func (es *EvtStream) Hook(f func(Event)) { + es.hook = f +} + +func (es *EvtStream) Loop() { + for e := range es.stream { + switch e.Path { + case "/sig/stoploop": + return + } + go func(a Event) { + es.RLock() + defer es.RUnlock() + if pattern := es.match(a.Path); pattern != "" { + es.Handlers[pattern](a) + } + }(e) + if es.hook != nil { + es.hook(e) + } + } +} + +func (es *EvtStream) StopLoop() { + go func() { + e := Event{ + Path: "/sig/stoploop", + } + es.sigStopLoop <- e + }() +} + +func Merge(name string, ec chan Event) { + DefaultEvtStream.Merge(name, ec) +} + +func Handle(path string, handler func(Event)) { + DefaultEvtStream.Handle(path, handler) +} + +func Loop() { + DefaultEvtStream.Loop() +} + +func StopLoop() { + DefaultEvtStream.StopLoop() +} + +type EvtTimer struct { + Duration time.Duration + Count uint64 +} + +func NewTimerCh(du time.Duration) chan Event { + t := make(chan Event) + + go func(a chan Event) { + n := uint64(0) + for { + n++ + time.Sleep(du) + e := Event{} + e.Type = "timer" + e.Path = "/timer/" + du.String() + e.Time = time.Now().Unix() + e.Data = EvtTimer{ + Duration: du, + Count: n, + } + t <- e + + } + }(t) + return t +} + +var DefualtHandler = func(e Event) { +} + +var usrEvtCh = make(chan Event) + +func SendCustomEvt(path string, data interface{}) { + e := Event{} + e.Path = path + e.Data = data + e.Time = time.Now().Unix() + usrEvtCh <- e +} diff --git a/vendor/github.com/gizak/termui/gauge.go b/vendor/github.com/gizak/termui/gauge.go new file mode 100644 index 000000000..01af0ea7c --- /dev/null +++ b/vendor/github.com/gizak/termui/gauge.go @@ -0,0 +1,109 @@ +// Copyright 2016 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.BorderLabel = "Slim Gauge" + g.BarColor = termui.ColorRed + g.PercentColor = termui.ColorBlue +*/ + +const ColorUndef Attribute = Attribute(^uint16(0)) + +type Gauge struct { + Block + Percent int + BarColor Attribute + PercentColor Attribute + PercentColorHighlighted Attribute + Label string + LabelAlign Align +} + +// NewGauge return a new gauge with current theme. +func NewGauge() *Gauge { + g := &Gauge{ + Block: *NewBlock(), + PercentColor: ThemeAttr("gauge.percent.fg"), + BarColor: ThemeAttr("gauge.bar.bg"), + Label: "{{percent}}%", + LabelAlign: AlignCenter, + PercentColorHighlighted: ColorUndef, + } + + g.Width = 12 + g.Height = 5 + return g +} + +// Buffer implements Bufferer interface. +func (g *Gauge) Buffer() Buffer { + buf := g.Block.Buffer() + + // plot bar + w := g.Percent * g.innerArea.Dx() / 100 + for i := 0; i < g.innerArea.Dy(); i++ { + for j := 0; j < w; j++ { + c := Cell{} + c.Ch = ' ' + c.Bg = g.BarColor + if c.Bg == ColorDefault { + c.Bg |= AttrReverse + } + buf.Set(g.innerArea.Min.X+j, g.innerArea.Min.Y+i, c) + } + } + + // plot percentage + s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1) + pry := g.innerArea.Min.Y + g.innerArea.Dy()/2 + rs := str2runes(s) + var pos int + switch g.LabelAlign { + case AlignLeft: + pos = 0 + + case AlignCenter: + pos = (g.innerArea.Dx() - strWidth(s)) / 2 + + case AlignRight: + pos = g.innerArea.Dx() - strWidth(s) - 1 + } + pos += g.innerArea.Min.X + + for i, v := range rs { + c := Cell{ + Ch: v, + Fg: g.PercentColor, + } + + if w+g.innerArea.Min.X > pos+i { + c.Bg = g.BarColor + if c.Bg == ColorDefault { + c.Bg |= AttrReverse + } + + if g.PercentColorHighlighted != ColorUndef { + c.Fg = g.PercentColorHighlighted + } + } else { + c.Bg = g.Block.Bg + } + + buf.Set(1+pos+i, pry, c) + } + return buf +} diff --git a/vendor/github.com/gizak/termui/glide.lock b/vendor/github.com/gizak/termui/glide.lock new file mode 100644 index 000000000..798e944d3 --- /dev/null +++ b/vendor/github.com/gizak/termui/glide.lock @@ -0,0 +1,14 @@ +hash: 67a478802ee1d122cf1df063c52458d074864900c96a5cc25dc6be4b7638eb1c +updated: 2016-04-06T21:16:00.849048757-04:00 +imports: +- name: github.com/mattn/go-runewidth + version: d6bea18f789704b5f83375793155289da36a3c7f +- name: github.com/mitchellh/go-wordwrap + version: ad45545899c7b13c020ea92b2072220eefad42b8 +- name: github.com/nsf/termbox-go + version: 362329b0aa6447eadd52edd8d660ec1dff470295 +- name: golang.org/x/net + version: af4fee9d05b66edc24197d189e6118f8ebce8c2b + subpackages: + - websocket +devImports: [] diff --git a/vendor/github.com/gizak/termui/glide.yaml b/vendor/github.com/gizak/termui/glide.yaml new file mode 100644 index 000000000..d8e445254 --- /dev/null +++ b/vendor/github.com/gizak/termui/glide.yaml @@ -0,0 +1,8 @@ +package: github.com/gizak/termui +import: +- package: github.com/mattn/go-runewidth +- package: github.com/mitchellh/go-wordwrap +- package: github.com/nsf/termbox-go +- package: golang.org/x/net + subpackages: + - websocket diff --git a/vendor/github.com/gizak/termui/grid.go b/vendor/github.com/gizak/termui/grid.go new file mode 100644 index 000000000..364442e02 --- /dev/null +++ b/vendor/github.com/gizak/termui/grid.go @@ -0,0 +1,279 @@ +// Copyright 2016 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() Buffer { + merged := NewBuffer() + + if r.isRenderableLeaf() { + return r.Widget.Buffer() + } + + // for those are not leaves but have a renderable widget + if r.Widget != nil { + merged.Merge(r.Widget.Buffer()) + } + + // collect buffer from children + if !r.isLeaf() { + for _, c := range r.Cols { + merged.Merge(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() Buffer { + buf := NewBuffer() + + for _, r := range g.Rows { + buf.Merge(r.Buffer()) + } + return buf +} + +var Body *Grid diff --git a/vendor/github.com/gizak/termui/helper.go b/vendor/github.com/gizak/termui/helper.go new file mode 100644 index 000000000..308f6c1db --- /dev/null +++ b/vendor/github.com/gizak/termui/helper.go @@ -0,0 +1,222 @@ +// Copyright 2016 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 ( + "regexp" + "strings" + + 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 + +// 8 basic clolrs +const ( + ColorDefault Attribute = iota + ColorBlack + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +//Have a constant that defines number of colors +const NumberofColors = 8 + +// Text style +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) +} + +// Here for backwards-compatibility. +func trimStr2Runes(s string, w int) []rune { + return TrimStr2Runes(s, w) +} + +// TrimStr2Runes trims string to w[-1 rune], appends …, and returns the runes +// of that string if string is grather then n. If string is small then w, +// return the runes. +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) +} + +// TrimStrIfAppropriate trim string to "s[:-1] + …" +// if string > width otherwise return string +func TrimStrIfAppropriate(s string, w int) string { + if w <= 0 { + return "" + } + + sw := rw.StringWidth(s) + if sw > w { + return rw.Truncate(s, w, dot) + } + + return s +} + +func strWidth(s string) int { + return rw.StringWidth(s) +} + +func charWidth(ch rune) int { + return rw.RuneWidth(ch) +} + +var whiteSpaceRegex = regexp.MustCompile(`\s`) + +// StringToAttribute converts text to a termui attribute. You may specifiy more +// then one attribute like that: "BLACK, BOLD, ...". All whitespaces +// are ignored. +func StringToAttribute(text string) Attribute { + text = whiteSpaceRegex.ReplaceAllString(strings.ToLower(text), "") + attributes := strings.Split(text, ",") + result := Attribute(0) + + for _, theAttribute := range attributes { + var match Attribute + switch theAttribute { + case "reset", "default": + match = ColorDefault + + case "black": + match = ColorBlack + + case "red": + match = ColorRed + + case "green": + match = ColorGreen + + case "yellow": + match = ColorYellow + + case "blue": + match = ColorBlue + + case "magenta": + match = ColorMagenta + + case "cyan": + match = ColorCyan + + case "white": + match = ColorWhite + + case "bold": + match = AttrBold + + case "underline": + match = AttrUnderline + + case "reverse": + match = AttrReverse + } + + result |= match + } + + return result +} + +// TextCells returns a coloured text cells []Cell +func TextCells(s string, fg, bg Attribute) []Cell { + cs := make([]Cell, 0, len(s)) + + // sequence := MarkdownTextRendererFactory{}.TextRenderer(s).Render(fg, bg) + // runes := []rune(sequence.NormalizedText) + runes := str2runes(s) + + for n := range runes { + // point, _ := sequence.PointAt(n, 0, 0) + // cs = append(cs, Cell{point.Ch, point.Fg, point.Bg}) + cs = append(cs, Cell{runes[n], fg, bg}) + } + return cs +} + +// Width returns the actual screen space the cell takes (usually 1 or 2). +func (c Cell) Width() int { + return charWidth(c.Ch) +} + +// Copy return a copy of c +func (c Cell) Copy() Cell { + return c +} + +// TrimTxCells trims the overflowed text cells sequence. +func TrimTxCells(cs []Cell, w int) []Cell { + if len(cs) <= w { + return cs + } + return cs[:w] +} + +// DTrimTxCls trims the overflowed text cells sequence and append dots at the end. +func DTrimTxCls(cs []Cell, w int) []Cell { + l := len(cs) + if l <= 0 { + return []Cell{} + } + + rt := make([]Cell, 0, w) + csw := 0 + for i := 0; i < l && csw <= w; i++ { + c := cs[i] + cw := c.Width() + + if cw+csw < w { + rt = append(rt, c) + csw += cw + } else { + rt = append(rt, Cell{'…', c.Fg, c.Bg}) + break + } + } + + return rt +} + +func CellsToStr(cs []Cell) string { + str := "" + for _, c := range cs { + str += string(c.Ch) + } + return str +} diff --git a/vendor/github.com/gizak/termui/linechart.go b/vendor/github.com/gizak/termui/linechart.go new file mode 100644 index 000000000..81834efdb --- /dev/null +++ b/vendor/github.com/gizak/termui/linechart.go @@ -0,0 +1,331 @@ +// Copyright 2016 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.BorderLabel = "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 + axisYLabelGap int + axisXLabelGap int + topValue float64 + bottomValue float64 + labelX [][]rune + labelY [][]rune + labelYSpace int + maxY float64 + minY float64 + autoLabels bool +} + +// NewLineChart returns a new LineChart with current theme. +func NewLineChart() *LineChart { + lc := &LineChart{Block: *NewBlock()} + lc.AxesColor = ThemeAttr("linechart.axes.fg") + lc.LineColor = ThemeAttr("linechart.line.fg") + lc.Mode = "braille" + lc.DotStyle = '•' + lc.axisXLabelGap = 2 + lc.axisYLabelGap = 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() Buffer { + buf := NewBuffer() + + // 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 { + c := Cell{ + Ch: braillePatterns[[2]int{m0, m1}], + Bg: lc.Bg, + Fg: lc.LineColor, + } + y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0 + x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + buf.Set(x, y, c) + } else { + c0 := Cell{Ch: lSingleBraille[m0], + Fg: lc.LineColor, + Bg: lc.Bg} + x0 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + y0 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0 + buf.Set(x0, y0, c0) + + c1 := Cell{Ch: rSingleBraille[m1], + Fg: lc.LineColor, + Bg: lc.Bg} + x1 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + y1 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b1 + buf.Set(x1, y1, c1) + } + + } + return buf +} + +func (lc *LineChart) renderDot() Buffer { + buf := NewBuffer() + for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ { + c := Cell{ + Ch: lc.DotStyle, + Fg: lc.LineColor, + Bg: lc.Bg, + } + x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5) + buf.Set(x, y, c) + } + + return buf +} + +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.axisXLabelGap + } 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.axisXLabelGap + + } + } +} + +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.axisYLabelGap + 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.autoLabels { + lc.autoLabels = true + 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.innerArea.Dx() + if lc.Mode == "braille" { + vrange = 2 * lc.innerArea.Dx() + } + 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.innerArea.Dy() - 2 + lc.calcLabelY() + + lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace + lc.calcLabelX() + + lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace + lc.drawingY = lc.innerArea.Min.Y +} + +func (lc *LineChart) plotAxes() Buffer { + buf := NewBuffer() + + origY := lc.innerArea.Min.Y + lc.innerArea.Dy() - 2 + origX := lc.innerArea.Min.X + lc.labelYSpace + + buf.Set(origX, origY, Cell{Ch: ORIGIN, Fg: lc.AxesColor, Bg: lc.Bg}) + + for x := origX + 1; x < origX+lc.axisXWidth; x++ { + buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg}) + } + + for dy := 1; dy <= lc.axisYHeight; dy++ { + buf.Set(origX, origY-dy, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg}) + } + + // x label + oft := 0 + for _, rs := range lc.labelX { + if oft+len(rs) > lc.axisXWidth { + break + } + for j, r := range rs { + c := Cell{ + Ch: r, + Fg: lc.AxesColor, + Bg: lc.Bg, + } + x := origX + oft + j + y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 1 + buf.Set(x, y, c) + } + oft += len(rs) + lc.axisXLabelGap + } + + // y labels + for i, rs := range lc.labelY { + for j, r := range rs { + buf.Set( + lc.innerArea.Min.X+j, + origY-i*(lc.axisYLabelGap+1), + Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg}) + } + } + + return buf +} + +// Buffer implements Bufferer interface. +func (lc *LineChart) Buffer() Buffer { + buf := lc.Block.Buffer() + + if lc.Data == nil || len(lc.Data) == 0 { + return buf + } + lc.calcLayout() + buf.Merge(lc.plotAxes()) + + if lc.Mode == "dot" { + buf.Merge(lc.renderDot()) + } else { + buf.Merge(lc.renderBraille()) + } + + return buf +} diff --git a/vendor/github.com/gizak/termui/linechart_others.go b/vendor/github.com/gizak/termui/linechart_others.go new file mode 100644 index 000000000..7e2e66b3e --- /dev/null +++ b/vendor/github.com/gizak/termui/linechart_others.go @@ -0,0 +1,11 @@ +// Copyright 2016 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/vendor/github.com/gizak/termui/linechart_windows.go b/vendor/github.com/gizak/termui/linechart_windows.go new file mode 100644 index 000000000..1478b5ce3 --- /dev/null +++ b/vendor/github.com/gizak/termui/linechart_windows.go @@ -0,0 +1,11 @@ +// Copyright 2016 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/vendor/github.com/gizak/termui/list.go b/vendor/github.com/gizak/termui/list.go new file mode 100644 index 000000000..0029d2622 --- /dev/null +++ b/vendor/github.com/gizak/termui/list.go @@ -0,0 +1,89 @@ +// Copyright 2016 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.BorderLabel = "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 = ThemeAttr("list.item.fg") + l.ItemBgColor = ThemeAttr("list.item.bg") + return l +} + +// Buffer implements Bufferer interface. +func (l *List) Buffer() Buffer { + buf := l.Block.Buffer() + + switch l.Overflow { + case "wrap": + cs := DefaultTxBuilder.Build(strings.Join(l.Items, "\n"), l.ItemFgColor, l.ItemBgColor) + i, j, k := 0, 0, 0 + for i < l.innerArea.Dy() && k < len(cs) { + w := cs[k].Width() + if cs[k].Ch == '\n' || j+w > l.innerArea.Dx() { + i++ + j = 0 + if cs[k].Ch == '\n' { + k++ + } + continue + } + buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, cs[k]) + + k++ + j++ + } + + case "hidden": + trimItems := l.Items + if len(trimItems) > l.innerArea.Dy() { + trimItems = trimItems[:l.innerArea.Dy()] + } + for i, v := range trimItems { + cs := DTrimTxCls(DefaultTxBuilder.Build(v, l.ItemFgColor, l.ItemBgColor), l.innerArea.Dx()) + j := 0 + for _, vv := range cs { + w := vv.Width() + buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, vv) + j += w + } + } + } + return buf +} diff --git a/vendor/github.com/gizak/termui/mbarchart.go b/vendor/github.com/gizak/termui/mbarchart.go new file mode 100644 index 000000000..6828e6533 --- /dev/null +++ b/vendor/github.com/gizak/termui/mbarchart.go @@ -0,0 +1,242 @@ +// Copyright 2016 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.BorderLabel = "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] = ThemeAttr("mbarchart.bar.bg") + bc.NumColor[0] = ThemeAttr("mbarchart.num.fg") + bc.TextColor = ThemeAttr("mbarchart.text.fg") + bc.BarGap = 1 + bc.BarWidth = 3 + return bc +} + +func (bc *MBarChart) layout() { + bc.numBar = bc.innerArea.Dx() / (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.innerArea.Dy()-2) + } else { + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1) + } + +} + +func (bc *MBarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *MBarChart) Buffer() Buffer { + buf := 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++ { + c := Cell{ + Ch: ' ', + Bg: bc.BarColor[i1], + } + if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent! + c.Bg |= AttrReverse + } + x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph + buf.Set(x, y, c) + + } + } + ph += h + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + c := Cell{ + Ch: bc.labels[i][j], + Bg: bc.Bg, + Fg: bc.TextColor, + } + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1 + x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k + buf.Set(x, y, c) + 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++ { + c := Cell{ + Ch: bc.dataNum[i1][i][j], + Fg: bc.NumColor[i1], + Bg: bc.BarColor[i1], + } + if bc.BarColor[i1] == ColorDefault { // the same as above + c.Bg |= AttrReverse + } + if h == 0 { + c.Bg = bc.Bg + } + x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph + buf.Set(x, y, c) + } + ph += h + } + } + + if bc.ShowScale { + //Currently bar graph only supprts data range from 0 to MAX + //Plot 0 + c := Cell{ + Ch: '0', + Bg: bc.Bg, + Fg: bc.TextColor, + } + + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 + x := bc.X + buf.Set(x, y, c) + + //Plot the maximum sacle value + for i := 0; i < len(bc.maxScale); i++ { + c := Cell{ + Ch: bc.maxScale[i], + Bg: bc.Bg, + Fg: bc.TextColor, + } + + y := bc.innerArea.Min.Y + x := bc.X + i + + buf.Set(x, y, c) + } + + } + + return buf +} diff --git a/vendor/github.com/gizak/termui/mkdocs.yml b/vendor/github.com/gizak/termui/mkdocs.yml new file mode 100644 index 000000000..2ab45f064 --- /dev/null +++ b/vendor/github.com/gizak/termui/mkdocs.yml @@ -0,0 +1,28 @@ +pages: +- Home: 'index.md' +- Quickstart: 'quickstart.md' +- Recipes: 'recipes.md' +- References: + - Layouts: 'layouts.md' + - Components: 'components.md' + - Events: 'events.md' + - Themes: 'themes.md' +- Versions: 'versions.md' +- About: 'about.md' + +site_name: termui +repo_url: https://github.com/gizak/termui/ +site_description: 'termui user guide' +site_author: gizak + +docs_dir: '_docs' + +theme: readthedocs + +markdown_extensions: + - smarty + - admonition + - toc + +extra: + version: 1.0 diff --git a/vendor/github.com/gizak/termui/par.go b/vendor/github.com/gizak/termui/par.go new file mode 100644 index 000000000..78bffd091 --- /dev/null +++ b/vendor/github.com/gizak/termui/par.go @@ -0,0 +1,73 @@ +// Copyright 2016 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.BorderLabel = "Label" +*/ +type Par struct { + Block + Text string + TextFgColor Attribute + TextBgColor Attribute + WrapLength int // words wrap limit. Note it may not work properly with multi-width char +} + +// NewPar returns a new *Par with given text as its content. +func NewPar(s string) *Par { + return &Par{ + Block: *NewBlock(), + Text: s, + TextFgColor: ThemeAttr("par.text.fg"), + TextBgColor: ThemeAttr("par.text.bg"), + WrapLength: 0, + } +} + +// Buffer implements Bufferer interface. +func (p *Par) Buffer() Buffer { + buf := p.Block.Buffer() + + fg, bg := p.TextFgColor, p.TextBgColor + cs := DefaultTxBuilder.Build(p.Text, fg, bg) + + // wrap if WrapLength set + if p.WrapLength < 0 { + cs = wrapTx(cs, p.Width-2) + } else if p.WrapLength > 0 { + cs = wrapTx(cs, p.WrapLength) + } + + y, x, n := 0, 0, 0 + for y < p.innerArea.Dy() && n < len(cs) { + w := cs[n].Width() + if cs[n].Ch == '\n' || x+w > p.innerArea.Dx() { + y++ + x = 0 // set x = 0 + if cs[n].Ch == '\n' { + n++ + } + + if y >= p.innerArea.Dy() { + buf.Set(p.innerArea.Min.X+p.innerArea.Dx()-1, + p.innerArea.Min.Y+p.innerArea.Dy()-1, + Cell{Ch: '…', Fg: p.TextFgColor, Bg: p.TextBgColor}) + break + } + continue + } + + buf.Set(p.innerArea.Min.X+x, p.innerArea.Min.Y+y, cs[n]) + + n++ + x += w + } + + return buf +} diff --git a/vendor/github.com/gizak/termui/pos.go b/vendor/github.com/gizak/termui/pos.go new file mode 100644 index 000000000..2046dce5a --- /dev/null +++ b/vendor/github.com/gizak/termui/pos.go @@ -0,0 +1,78 @@ +// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Align is the position of the gauge's label. +type Align uint + +// All supported positions. +const ( + AlignNone Align = 0 + AlignLeft Align = 1 << iota + AlignRight + AlignBottom + AlignTop + AlignCenterVertical + AlignCenterHorizontal + AlignCenter = AlignCenterVertical | AlignCenterHorizontal +) + +func AlignArea(parent, child image.Rectangle, a Align) image.Rectangle { + w, h := child.Dx(), child.Dy() + + // parent center + pcx, pcy := parent.Min.X+parent.Dx()/2, parent.Min.Y+parent.Dy()/2 + // child center + ccx, ccy := child.Min.X+child.Dx()/2, child.Min.Y+child.Dy()/2 + + if a&AlignLeft == AlignLeft { + child.Min.X = parent.Min.X + child.Max.X = child.Min.X + w + } + + if a&AlignRight == AlignRight { + child.Max.X = parent.Max.X + child.Min.X = child.Max.X - w + } + + if a&AlignBottom == AlignBottom { + child.Max.Y = parent.Max.Y + child.Min.Y = child.Max.Y - h + } + + if a&AlignTop == AlignRight { + child.Min.Y = parent.Min.Y + child.Max.Y = child.Min.Y + h + } + + if a&AlignCenterHorizontal == AlignCenterHorizontal { + child.Min.X += pcx - ccx + child.Max.X = child.Min.X + w + } + + if a&AlignCenterVertical == AlignCenterVertical { + child.Min.Y += pcy - ccy + child.Max.Y = child.Min.Y + h + } + + return child +} + +func MoveArea(a image.Rectangle, dx, dy int) image.Rectangle { + a.Min.X += dx + a.Max.X += dx + a.Min.Y += dy + a.Max.Y += dy + return a +} + +var termWidth int +var termHeight int + +func TermRect() image.Rectangle { + return image.Rect(0, 0, termWidth, termHeight) +} diff --git a/vendor/github.com/gizak/termui/render.go b/vendor/github.com/gizak/termui/render.go new file mode 100644 index 000000000..36544f063 --- /dev/null +++ b/vendor/github.com/gizak/termui/render.go @@ -0,0 +1,135 @@ +// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "image" + "sync" + "time" + + tm "github.com/nsf/termbox-go" +) + +// Bufferer should be implemented by all renderable components. +type Bufferer interface { + Buffer() Buffer +} + +// 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 { + if err := tm.Init(); err != nil { + return err + } + + sysEvtChs = make([]chan Event, 0) + go hookTermboxEvt() + + renderJobs = make(chan []Bufferer) + //renderLock = new(sync.RWMutex) + + Body = NewGrid() + Body.X = 0 + Body.Y = 0 + Body.BgColor = ThemeAttr("bg") + Body.Width = TermWidth() + + DefaultEvtStream.Init() + DefaultEvtStream.Merge("termbox", NewSysEvtCh()) + DefaultEvtStream.Merge("timer", NewTimerCh(time.Second)) + DefaultEvtStream.Merge("custom", usrEvtCh) + + DefaultEvtStream.Handle("/", DefualtHandler) + DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) { + w := e.Data.(EvtWnd) + Body.Width = w.Width + }) + + DefaultWgtMgr = NewWgtMgr() + DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook()) + + go func() { + for bs := range renderJobs { + render(bs...) + } + }() + + return nil +} + +// Close finalizes termui library, +// should be called after successful initialization when termui's functionality isn't required anymore. +func Close() { + tm.Close() +} + +var renderLock sync.Mutex + +func termSync() { + renderLock.Lock() + tm.Sync() + termWidth, termHeight = tm.Size() + renderLock.Unlock() +} + +// TermWidth returns the current terminal's width. +func TermWidth() int { + termSync() + return termWidth +} + +// TermHeight returns the current terminal's height. +func TermHeight() int { + termSync() + return termHeight +} + +// Render renders all Bufferer in the given order from left to right, +// right could overlap on left ones. +func render(bs ...Bufferer) { + + for _, b := range bs { + + buf := b.Buffer() + // set cels in buf + for p, c := range buf.CellMap { + if p.In(buf.Area) { + + tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg)) + + } + } + + } + + renderLock.Lock() + // render + tm.Flush() + renderLock.Unlock() +} + +func Clear() { + tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg"))) +} + +func clearArea(r image.Rectangle, bg Attribute) { + for i := r.Min.X; i < r.Max.X; i++ { + for j := r.Min.Y; j < r.Max.Y; j++ { + tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg)) + } + } +} + +func ClearArea(r image.Rectangle, bg Attribute) { + clearArea(r, bg) + tm.Flush() +} + +var renderJobs chan []Bufferer + +func Render(bs ...Bufferer) { + //go func() { renderJobs <- bs }() + renderJobs <- bs +} diff --git a/vendor/github.com/gizak/termui/sparkline.go b/vendor/github.com/gizak/termui/sparkline.go new file mode 100644 index 000000000..312ad9563 --- /dev/null +++ b/vendor/github.com/gizak/termui/sparkline.go @@ -0,0 +1,167 @@ +// Copyright 2016 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 + +// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers. +/* + 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: ThemeAttr("sparkline.title.fg"), + LineColor: ThemeAttr("sparkline.line.fg")} +} + +// 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.innerArea.Dx() + + // get how many lines gotta display + h := 0 + sl.displayLines = 0 + for _, v := range sl.Lines { + if h+v.displayHeight <= sl.innerArea.Dy() { + sl.displayLines++ + } else { + break + } + h += v.displayHeight + } + + for i := 0; i < sl.displayLines; i++ { + data := sl.Lines[i].Data + + max := 0 + for _, v := range data { + if max < v { + max = v + } + } + sl.Lines[i].max = max + if max != 0 { + sl.Lines[i].scale = float32(8*sl.Lines[i].Height) / float32(max) + } else { // when all negative + sl.Lines[i].scale = 0 + } + } +} + +// Buffer implements Bufferer interface. +func (sl *Sparklines) Buffer() Buffer { + buf := 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.innerArea.Dx() { + data = data[len(data)-sl.innerArea.Dx():] + } + + if l.Title != "" { + rs := trimStr2Runes(l.Title, sl.innerArea.Dx()) + oftX := 0 + for _, v := range rs { + w := charWidth(v) + c := Cell{ + Ch: v, + Fg: l.TitleColor, + Bg: sl.Bg, + } + x := sl.innerArea.Min.X + oftX + y := sl.innerArea.Min.Y + oftY + buf.Set(x, y, c) + oftX += w + } + } + + for j, v := range data { + // display height of the data point, zero when data is negative + h := int(float32(v)*l.scale + 0.5) + if v < 0 { + h = 0 + } + + barCnt := h / 8 + barMod := h % 8 + for jj := 0; jj < barCnt; jj++ { + c := Cell{ + Ch: ' ', // => sparks[7] + Bg: l.LineColor, + } + x := sl.innerArea.Min.X + j + y := sl.innerArea.Min.Y + oftY + l.Height - jj + + //p.Bg = sl.BgColor + buf.Set(x, y, c) + } + if barMod != 0 { + c := Cell{ + Ch: sparks[barMod-1], + Fg: l.LineColor, + Bg: sl.Bg, + } + x := sl.innerArea.Min.X + j + y := sl.innerArea.Min.Y + oftY + l.Height - barCnt + buf.Set(x, y, c) + } + } + + oftY += l.displayHeight + } + + return buf +} diff --git a/vendor/github.com/gizak/termui/textbuilder.go b/vendor/github.com/gizak/termui/textbuilder.go new file mode 100644 index 000000000..06a019bed --- /dev/null +++ b/vendor/github.com/gizak/termui/textbuilder.go @@ -0,0 +1,278 @@ +// Copyright 2016 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 ( + "regexp" + "strings" + + "github.com/mitchellh/go-wordwrap" +) + +// TextBuilder is a minimal interface to produce text []Cell using specific syntax (markdown). +type TextBuilder interface { + Build(s string, fg, bg Attribute) []Cell +} + +// DefaultTxBuilder is set to be MarkdownTxBuilder. +var DefaultTxBuilder = NewMarkdownTxBuilder() + +// MarkdownTxBuilder implements TextBuilder interface, using markdown syntax. +type MarkdownTxBuilder struct { + baseFg Attribute + baseBg Attribute + plainTx []rune + markers []marker +} + +type marker struct { + st int + ed int + fg Attribute + bg Attribute +} + +var colorMap = map[string]Attribute{ + "red": ColorRed, + "blue": ColorBlue, + "black": ColorBlack, + "cyan": ColorCyan, + "yellow": ColorYellow, + "white": ColorWhite, + "default": ColorDefault, + "green": ColorGreen, + "magenta": ColorMagenta, +} + +var attrMap = map[string]Attribute{ + "bold": AttrBold, + "underline": AttrUnderline, + "reverse": AttrReverse, +} + +func rmSpc(s string) string { + reg := regexp.MustCompile(`\s+`) + return reg.ReplaceAllString(s, "") +} + +// readAttr translates strings like `fg-red,fg-bold,bg-white` to fg and bg Attribute +func (mtb MarkdownTxBuilder) readAttr(s string) (Attribute, Attribute) { + fg := mtb.baseFg + bg := mtb.baseBg + + updateAttr := func(a Attribute, attrs []string) Attribute { + for _, s := range attrs { + // replace the color + if c, ok := colorMap[s]; ok { + a &= 0xFF00 // erase clr 0 ~ 8 bits + a |= c // set clr + } + // add attrs + if c, ok := attrMap[s]; ok { + a |= c + } + } + return a + } + + ss := strings.Split(s, ",") + fgs := []string{} + bgs := []string{} + for _, v := range ss { + subs := strings.Split(v, "-") + if len(subs) > 1 { + if subs[0] == "fg" { + fgs = append(fgs, subs[1]) + } + if subs[0] == "bg" { + bgs = append(bgs, subs[1]) + } + } + } + + fg = updateAttr(fg, fgs) + bg = updateAttr(bg, bgs) + return fg, bg +} + +func (mtb *MarkdownTxBuilder) reset() { + mtb.plainTx = []rune{} + mtb.markers = []marker{} +} + +// parse streams and parses text into normalized text and render sequence. +func (mtb *MarkdownTxBuilder) parse(str string) { + rs := str2runes(str) + normTx := []rune{} + square := []rune{} + brackt := []rune{} + accSquare := false + accBrackt := false + cntSquare := 0 + + reset := func() { + square = []rune{} + brackt = []rune{} + accSquare = false + accBrackt = false + cntSquare = 0 + } + // pipe stacks into normTx and clear + rollback := func() { + normTx = append(normTx, square...) + normTx = append(normTx, brackt...) + reset() + } + // chop first and last + chop := func(s []rune) []rune { + return s[1 : len(s)-1] + } + + for i, r := range rs { + switch { + // stacking brackt + case accBrackt: + brackt = append(brackt, r) + if ')' == r { + fg, bg := mtb.readAttr(string(chop(brackt))) + st := len(normTx) + ed := len(normTx) + len(square) - 2 + mtb.markers = append(mtb.markers, marker{st, ed, fg, bg}) + normTx = append(normTx, chop(square)...) + reset() + } else if i+1 == len(rs) { + rollback() + } + // stacking square + case accSquare: + switch { + // squares closed and followed by a '(' + case cntSquare == 0 && '(' == r: + accBrackt = true + brackt = append(brackt, '(') + // squares closed but not followed by a '(' + case cntSquare == 0: + rollback() + if '[' == r { + accSquare = true + cntSquare = 1 + brackt = append(brackt, '[') + } else { + normTx = append(normTx, r) + } + // hit the end + case i+1 == len(rs): + square = append(square, r) + rollback() + case '[' == r: + cntSquare++ + square = append(square, '[') + case ']' == r: + cntSquare-- + square = append(square, ']') + // normal char + default: + square = append(square, r) + } + // stacking normTx + default: + if '[' == r { + accSquare = true + cntSquare = 1 + square = append(square, '[') + } else { + normTx = append(normTx, r) + } + } + } + + mtb.plainTx = normTx +} + +func wrapTx(cs []Cell, wl int) []Cell { + tmpCell := make([]Cell, len(cs)) + copy(tmpCell, cs) + + // get the plaintext + plain := CellsToStr(cs) + + // wrap + plainWrapped := wordwrap.WrapString(plain, uint(wl)) + + // find differences and insert + finalCell := tmpCell // finalcell will get the inserts and is what is returned + + plainRune := []rune(plain) + plainWrappedRune := []rune(plainWrapped) + trigger := "go" + plainRuneNew := plainRune + + for trigger != "stop" { + plainRune = plainRuneNew + for i := range plainRune { + if plainRune[i] == plainWrappedRune[i] { + trigger = "stop" + } else if plainRune[i] != plainWrappedRune[i] && plainWrappedRune[i] == 10 { + trigger = "go" + cell := Cell{10, 0, 0} + j := i - 0 + + // insert a cell into the []Cell in correct position + tmpCell[i] = cell + + // insert the newline into plain so we avoid indexing errors + plainRuneNew = append(plainRune, 10) + copy(plainRuneNew[j+1:], plainRuneNew[j:]) + plainRuneNew[j] = plainWrappedRune[j] + + // restart the inner for loop until plain and plain wrapped are + // the same; yeah, it's inefficient, but the text amounts + // should be small + break + + } else if plainRune[i] != plainWrappedRune[i] && + plainWrappedRune[i-1] == 10 && // if the prior rune is a newline + plainRune[i] == 32 { // and this rune is a space + trigger = "go" + // need to delete plainRune[i] because it gets rid of an extra + // space + plainRuneNew = append(plainRune[:i], plainRune[i+1:]...) + break + + } else { + trigger = "stop" // stops the outer for loop + } + } + } + + finalCell = tmpCell + + return finalCell +} + +// Build implements TextBuilder interface. +func (mtb MarkdownTxBuilder) Build(s string, fg, bg Attribute) []Cell { + mtb.baseFg = fg + mtb.baseBg = bg + mtb.reset() + mtb.parse(s) + cs := make([]Cell, len(mtb.plainTx)) + for i := range cs { + cs[i] = Cell{Ch: mtb.plainTx[i], Fg: fg, Bg: bg} + } + for _, mrk := range mtb.markers { + for i := mrk.st; i < mrk.ed; i++ { + cs[i].Fg = mrk.fg + cs[i].Bg = mrk.bg + } + } + + return cs +} + +// NewMarkdownTxBuilder returns a TextBuilder employing markdown syntax. +func NewMarkdownTxBuilder() TextBuilder { + return MarkdownTxBuilder{} +} diff --git a/vendor/github.com/gizak/termui/theme.go b/vendor/github.com/gizak/termui/theme.go new file mode 100644 index 000000000..c3ccda559 --- /dev/null +++ b/vendor/github.com/gizak/termui/theme.go @@ -0,0 +1,140 @@ +// Copyright 2016 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" + +/* +// 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 + TabActiveBg 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, + TabActiveBg: ColorMagenta, +} + +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 + } +} +*/ + +var ColorMap = map[string]Attribute{ + "fg": ColorWhite, + "bg": ColorDefault, + "border.fg": ColorWhite, + "label.fg": ColorGreen, + "par.fg": ColorYellow, + "par.label.bg": ColorWhite, +} + +func ThemeAttr(name string) Attribute { + return lookUpAttr(ColorMap, name) +} + +func lookUpAttr(clrmap map[string]Attribute, name string) Attribute { + + a, ok := clrmap[name] + if ok { + return a + } + + ns := strings.Split(name, ".") + for i := range ns { + nn := strings.Join(ns[i:len(ns)], ".") + a, ok = ColorMap[nn] + if ok { + break + } + } + + return a +} + +// 0<=r,g,b <= 5 +func ColorRGB(r, g, b int) Attribute { + within := func(n int) int { + if n < 0 { + return 0 + } + + if n > 5 { + return 5 + } + + return n + } + + r, b, g = within(r), within(b), within(g) + return Attribute(0x0f + 36*r + 6*g + b) +} diff --git a/vendor/github.com/gizak/termui/widget.go b/vendor/github.com/gizak/termui/widget.go new file mode 100644 index 000000000..35cf143a3 --- /dev/null +++ b/vendor/github.com/gizak/termui/widget.go @@ -0,0 +1,94 @@ +// Copyright 2016 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" + "sync" +) + +// event mixins +type WgtMgr map[string]WgtInfo + +type WgtInfo struct { + Handlers map[string]func(Event) + WgtRef Widget + Id string +} + +type Widget interface { + Id() string +} + +func NewWgtInfo(wgt Widget) WgtInfo { + return WgtInfo{ + Handlers: make(map[string]func(Event)), + WgtRef: wgt, + Id: wgt.Id(), + } +} + +func NewWgtMgr() WgtMgr { + wm := WgtMgr(make(map[string]WgtInfo)) + return wm + +} + +func (wm WgtMgr) AddWgt(wgt Widget) { + wm[wgt.Id()] = NewWgtInfo(wgt) +} + +func (wm WgtMgr) RmWgt(wgt Widget) { + wm.RmWgtById(wgt.Id()) +} + +func (wm WgtMgr) RmWgtById(id string) { + delete(wm, id) +} + +func (wm WgtMgr) AddWgtHandler(id, path string, h func(Event)) { + if w, ok := wm[id]; ok { + w.Handlers[path] = h + } +} + +func (wm WgtMgr) RmWgtHandler(id, path string) { + if w, ok := wm[id]; ok { + delete(w.Handlers, path) + } +} + +var counter struct { + sync.RWMutex + count int +} + +func GenId() string { + counter.Lock() + defer counter.Unlock() + + counter.count += 1 + return fmt.Sprintf("%d", counter.count) +} + +func (wm WgtMgr) WgtHandlersHook() func(Event) { + return func(e Event) { + for _, v := range wm { + if k := findMatch(v.Handlers, e.Path); k != "" { + v.Handlers[k](e) + } + } + } +} + +var DefaultWgtMgr WgtMgr + +func (b *Block) Handle(path string, handler func(Event)) { + if _, ok := DefaultWgtMgr[b.Id()]; !ok { + DefaultWgtMgr.AddWgt(b) + } + + DefaultWgtMgr.AddWgtHandler(b.Id(), path, handler) +} |