diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/gizak')
57 files changed, 1918 insertions, 1982 deletions
diff --git a/Godeps/_workspace/src/github.com/gizak/termui/.gitignore b/Godeps/_workspace/src/github.com/gizak/termui/.gitignore index daf913b1b..eb1369fd4 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/.gitignore +++ b/Godeps/_workspace/src/github.com/gizak/termui/.gitignore @@ -22,3 +22,4 @@ _testmain.go *.exe *.test *.prof +.DS_Store diff --git a/Godeps/_workspace/src/github.com/gizak/termui/README.md b/Godeps/_workspace/src/github.com/gizak/termui/README.md index b9bc3024d..0e1d41b08 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/README.md +++ b/Godeps/_workspace/src/github.com/gizak/termui/README.md @@ -1,55 +1,30 @@ # 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. +<img src="./_example/dashboard.gif" alt="demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)" width="80%"> -## 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. +`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. -## Introduction -Go terminal dashboard. 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!) -Cross-platform, easy to compile, and fully-customizable. - -__Demo:__ (cast under osx 10.10; Terminal.app; Menlo Regular 12pt.) +## Installation -<img src="./example/dashboard.gif" alt="demo" width="600"> +`master` mirrors v2 branch, to install: -__Grid layout:__ + go get -u github.com/gizak/termui -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... +For the compatible reason, you can choose to install the legacy version of `termui`: - // 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))) + go get gopkg.in/gizak/termui.v1 - // 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 +## Usage - go get github.com/gizak/termui +### Layout -## Usage +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. -Each component's layout is a bit like HTML block (box model), which has border and padding. +__Absolute layout__ -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). +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 send it to `.Render`. Let's demonstrate these by a code snippet: `````go import ui "github.com/gizak/termui" // <- ui shortcut, optional @@ -65,20 +40,20 @@ The `Border` property can be chosen to hide or display (with its border label), p.Height = 3 p.Width = 50 p.TextFgColor = ui.ColorWhite - p.Border.Label = "Text Box" - p.Border.FgColor = ui.ColorCyan + p.BorderLabel = "Text Box" + p.BorderFg = ui.ColorCyan g := ui.NewGauge() g.Percent = 50 g.Width = 50 g.Height = 3 g.Y = 11 - g.Border.Label = "Gauge" + g.BorderLabel = "Gauge" g.BarColor = ui.ColorRed - g.Border.FgColor = ui.ColorWhite - g.Border.LabelFgColor = ui.ColorCyan + g.BorderFg = ui.ColorWhite + g.BorderLabelFg = ui.ColorCyan - ui.Render(p, g) + ui.Render(p, g) // feel free to call Render, it's async and non-block // event handler... } @@ -86,62 +61,74 @@ The `Border` property can be chosen to hide or display (with its border label), 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"> +__Grid layout:__ -#### List -[demo code](https://github.com/gizak/termui/blob/master/example/list.go) +<img src="./_example/grid.gif" alt="grid" width="60%"> -<img src="./example/list.png" alt="list" type="image/png" width="200"> +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 Cols (Actually a Col is also a `Row` but with a widget endpoint attached). -#### Gauge -[demo code](https://github.com/gizak/termui/blob/master/example/gauge.go) +```go + import ui "github.com/gizak/termui" + // init and create widgets... -<img src="./example/gauge.png" alt="gauge" type="image/png" width="350"> + // 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))) -#### Line Chart -[demo code](https://github.com/gizak/termui/blob/master/example/linechart.go) + // calculate layout + ui.Body.Align() -<img src="./example/linechart.png" alt="linechart" type="image/png" width="450"> + ui.Render(ui.Body) +``` -#### Bar Chart -[demo code](https://github.com/gizak/termui/blob/master/example/barchart.go) +### Events -<img src="./example/barchart.png" alt="barchart" type="image/png" width="150"> +`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: -#### Mult-Bar / Stacked-Bar Chart -[demo code](https://github.com/gizak/termui/blob/master/example/mbarchart.go) +```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 + } + }) -<img src="./example/mbarchart.png" alt="barchart" type="image/png" width="150"> + ui.Loop() // block until StopLoop is called +``` -#### Sparklines -[demo code](https://github.com/gizak/termui/blob/master/example/sparklines.go) +### Widgets -<img src="./example/sparklines.png" alt="sparklines" type="image/png" width="350"> +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 @@ -150,10 +137,12 @@ The `helloworld` color scheme drops in some colors! ## TODO - [x] Grid layout -- [ ] Event system -- [ ] Canvas widget -- [ ] Refine APIs +- [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/Godeps/_workspace/src/github.com/gizak/termui/bar.go b/Godeps/_workspace/src/github.com/gizak/termui/barchart.go index 57bae0ae8..980e958e2 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/bar.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/barchart.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -39,16 +39,16 @@ type BarChart struct { // 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.BarColor = ThemeAttr("barchart.bar.bg") + bc.NumColor = ThemeAttr("barchart.num.fg") + bc.TextColor = ThemeAttr("barchart.text.fg") bc.BarGap = 1 bc.BarWidth = 3 return bc } func (bc *BarChart) layout() { - bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth) + bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth) bc.labels = make([][]rune, bc.numBar) bc.dataNum = make([][]rune, len(bc.Data)) @@ -69,7 +69,7 @@ func (bc *BarChart) layout() { bc.max = bc.Data[i] } } - bc.scale = float64(bc.max) / float64(bc.innerHeight-1) + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1) } func (bc *BarChart) SetMax(max int) { @@ -80,8 +80,8 @@ func (bc *BarChart) SetMax(max int) { } // Buffer implements Bufferer interface. -func (bc *BarChart) Buffer() []Point { - ps := bc.Block.Buffer() +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++ { @@ -90,46 +90,49 @@ func (bc *BarChart) Buffer() []Point { // plot bar for j := 0; j < bc.BarWidth; j++ { for k := 0; k < h; k++ { - p := Point{} - p.Ch = ' ' - p.Bg = bc.BarColor + c := Cell{ + Ch: ' ', + Bg: bc.BarColor, + } if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent! - p.Bg |= AttrReverse + c.Bg |= AttrReverse } - p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j - p.Y = bc.innerY + bc.innerHeight - 2 - k - ps = append(ps, p) + 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]) - 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) + 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++ { - p := Point{} - p.Ch = bc.dataNum[i][j] - p.Fg = bc.NumColor - p.Bg = bc.BarColor + c := Cell{ + Ch: bc.dataNum[i][j], + Fg: bc.NumColor, + Bg: bc.BarColor, + } if bc.BarColor == ColorDefault { // the same as above - p.Bg |= AttrReverse + c.Bg |= AttrReverse } if h == 0 { - p.Bg = bc.BgColor + c.Bg = bc.Bg } - p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j - p.Y = bc.innerY + bc.innerHeight - 2 - ps = append(ps, p) + 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 bc.Block.chopOverflow(ps) + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/block.go b/Godeps/_workspace/src/github.com/gizak/termui/block.go index 953136596..418738c8d 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/block.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/block.go @@ -1,142 +1,240 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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 labeledBorder - IsDisplay bool - HasBorder bool - BgColor Attribute + 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 - innerWidth int - innerHeight int - innerX int - innerY 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 { - 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++ - } + 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 +} - if d.innerHeight < 0 { - d.innerHeight = 0 - } - if d.innerWidth < 0 { - d.innerWidth = 0 - } +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 (d *Block) InnerBounds() (x, y, width, height int) { - d.align() - return d.innerX, d.innerY, d.innerWidth, d.innerHeight +func (b *Block) InnerBounds() image.Rectangle { + b.Align() + return b.innerArea } // Buffer implements Bufferer interface. // Draw background and border (if any). -func (d *Block) Buffer() []Point { - d.align() +func (b *Block) Buffer() Buffer { + b.Align() - ps := []Point{} - if !d.IsDisplay { - return ps - } + buf := NewBuffer() + buf.SetArea(b.area) + buf.Fill(' ', ColorDefault, b.Bg) - if d.HasBorder { - ps = d.Border.Buffer() - } + b.drawBorder(buf) + b.drawBorderLabel(buf) - 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 + return buf } // GetHeight implements GridBufferer. // It returns current height of the block. -func (d Block) GetHeight() int { - return d.Height +func (b Block) GetHeight() int { + return b.Height } // SetX implements GridBufferer interface, which sets block's x position. -func (d *Block) SetX(x int) { - d.X = x +func (b *Block) SetX(x int) { + b.X = x } // SetY implements GridBufferer interface, it sets y position for block. -func (d *Block) SetY(y int) { - d.Y = y +func (b *Block) SetY(y int) { + b.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 +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/Godeps/_workspace/src/github.com/gizak/termui/box_others.go b/Godeps/_workspace/src/github.com/gizak/termui/block_common.go index bcc3d7ded..0f972cbbd 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/box_others.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/block_common.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -12,3 +12,9 @@ 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/Godeps/_workspace/src/github.com/gizak/termui/block_test.go b/Godeps/_workspace/src/github.com/gizak/termui/block_test.go deleted file mode 100644 index 2de205b21..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/block_test.go +++ /dev/null @@ -1,46 +0,0 @@ -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_windows.go b/Godeps/_workspace/src/github.com/gizak/termui/block_windows.go index dd39019fe..4dbc017de 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/box_windows.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/block_windows.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. diff --git a/Godeps/_workspace/src/github.com/gizak/termui/box.go b/Godeps/_workspace/src/github.com/gizak/termui/box.go deleted file mode 100644 index 1dcfd8692..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/box.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -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/buffer.go b/Godeps/_workspace/src/github.com/gizak/termui/buffer.go new file mode 100644 index 000000000..60e77863a --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/gizak/termui/canvas.go b/Godeps/_workspace/src/github.com/gizak/termui/canvas.go index 614635ee4..4173780f3 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/canvas.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/canvas.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -63,12 +63,10 @@ func (c Canvas) Unset(x, y int) { } // Buffer returns un-styled points -func (c Canvas) Buffer() []Point { - ps := make([]Point, len(c)) - i := 0 +func (c Canvas) Buffer() Buffer { + buf := NewBuffer() for k, v := range c { - ps[i] = newPoint(v+brailleBase, k[0], k[1]) - i++ + buf.Set(k[0], k[1], Cell{Ch: v + brailleBase}) } - return ps + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go b/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go deleted file mode 100644 index 021949ced..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/canvas_test.go +++ /dev/null @@ -1,55 +0,0 @@ -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/config b/Godeps/_workspace/src/github.com/gizak/termui/config new file mode 100644 index 000000000..18fd6a401 --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/gizak/termui/debug/debuger.go b/Godeps/_workspace/src/github.com/gizak/termui/debug/debuger.go new file mode 100644 index 000000000..f723b9686 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/debug/debuger.go @@ -0,0 +1,117 @@ +// 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 debug + +import ( + "fmt" + "net/http" + + "golang.org/x/net/websocket" +) + +type Server struct { + Port string + Addr string + Path string + Msg chan string + chs []chan string +} + +type Client struct { + Port string + Addr string + Path string + ws *websocket.Conn +} + +var defaultPort = ":8080" + +func NewServer() *Server { + return &Server{ + Port: defaultPort, + Addr: "localhost", + Path: "/echo", + Msg: make(chan string), + chs: make([]chan string, 0), + } +} + +func NewClient() Client { + return Client{ + Port: defaultPort, + Addr: "localhost", + Path: "/echo", + } +} + +func (c Client) ConnectAndListen() error { + ws, err := websocket.Dial("ws://"+c.Addr+c.Port+c.Path, "", "http://"+c.Addr) + if err != nil { + return err + } + defer ws.Close() + + var m string + for { + err := websocket.Message.Receive(ws, &m) + if err != nil { + fmt.Print(err) + return err + } + fmt.Print(m) + } +} + +func (s *Server) ListenAndServe() error { + http.Handle(s.Path, websocket.Handler(func(ws *websocket.Conn) { + defer ws.Close() + + mc := make(chan string) + s.chs = append(s.chs, mc) + + for m := range mc { + websocket.Message.Send(ws, m) + } + })) + + go func() { + for msg := range s.Msg { + for _, c := range s.chs { + go func(a chan string) { + a <- msg + }(c) + } + } + }() + + return http.ListenAndServe(s.Port, nil) +} + +func (s *Server) Log(msg string) { + go func() { s.Msg <- msg }() +} + +func (s *Server) Logf(format string, a ...interface{}) { + s.Log(fmt.Sprintf(format, a...)) +} + +var DefaultServer = NewServer() +var DefaultClient = NewClient() + +func ListenAndServe() error { + return DefaultServer.ListenAndServe() +} + +func ConnectAndListen() error { + return DefaultClient.ConnectAndListen() +} + +func Log(msg string) { + DefaultServer.Log(msg) +} + +func Logf(format string, a ...interface{}) { + DefaultServer.Logf(format, a...) +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/doc.go b/Godeps/_workspace/src/github.com/gizak/termui/doc.go index 43f886f55..b80e46415 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/doc.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/doc.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. diff --git a/Godeps/_workspace/src/github.com/gizak/termui/events.go b/Godeps/_workspace/src/github.com/gizak/termui/events.go index 23a189b56..177bbb4b0 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/events.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/events.go @@ -1,219 +1,316 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. -// -// 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" +import ( + "path" + "strconv" + "sync" + "time" -/***********************************termbox-go**************************************/ - -type ( - EventType uint8 - Modifier uint8 - Key uint16 + "github.com/nsf/termbox-go" ) -// 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 -) + Type string + Path string + From string + To string + Data interface{} + Time int64 +} -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 -) +var sysEvtChs []chan Event -// Alt modifier constant, see Event.Mod field and SetInputMode function. -const ( - ModAlt Modifier = 0x01 -) +type EvtKbd struct { + KeyStr string +} -// Event type. See Event.Type field. -const ( - EventKey EventType = iota - EventResize - EventMouse - EventError - EventInterrupt - EventRaw - EventNone -) +func evtKbd(e termbox.Event) EvtKbd { + ek := EvtKbd{} -/**************************************end**************************************/ + k := string(e.Ch) + pre := "" + mod := "" -// 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 + 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] + } - return event + 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 } -var evtChs = make([]chan Event, 0) +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 +} -// 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 +type EvtWnd struct { + Width int + Height int } -// 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) - } +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) }() } -/* -// EventHandlers is a handler sequence -var EventHandlers []func(Event) +func cleanPath(p string) string { + if p == "" { + return "/" + } + if p[0] != '/' { + p = "/" + p + } + return path.Clean(p) +} -var signalQuit = make(chan bool) +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 -// Quit sends quit signal to terminate termui -func Quit() { - signalQuit <- true + go func(a chan Event) { + for n := range a { + n.From = name + es.stream <- n + } + es.wg.Done() + }(ec) } -// Wait listening to signalQuit, block operation. -func Wait() { - <-signalQuit +func (es *EvtStream) Handle(path string, handler func(Event)) { + es.Handlers[cleanPath(path)] = handler } -// RegEvtHandler register function into TSEventHandler sequence. -func RegEvtHandler(fn func(Event)) { - EventHandlers = append(EventHandlers, fn) +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 + } -// EventLoop handles all events and -// redirects every event to callbacks in EventHandlers -func EventLoop() { - evt := make(chan termbox.Event) +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() { - for { - evt <- termbox.PollEvent() + e := Event{ + Path: "/sig/stoploop", } + es.sigStopLoop <- e }() +} - for { - select { - case c := <-signalQuit: - defer func() { signalQuit <- c }() - return - case e := <-evt: - for _, fn := range EventHandlers { - fn(uiEvt(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/Godeps/_workspace/src/github.com/gizak/termui/events_test.go b/Godeps/_workspace/src/github.com/gizak/termui/events_test.go deleted file mode 100644 index 1137b1d26..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/events_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. -// -// 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 deleted file mode 100644 index 83947f580..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import "github.com/gizak/termui" - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - bc := termui.NewBarChart() - data := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6} - bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} - bc.Border.Label = "Bar Chart" - bc.Data = data - bc.Width = 26 - bc.Height = 10 - bc.DataLabels = bclabels - bc.TextColor = termui.ColorGreen - bc.BarColor = termui.ColorRed - bc.NumColor = termui.ColorYellow - - termui.Render(bc) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png Binary files differdeleted file mode 100644 index a37912f7f..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/barchart.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif Binary files differdeleted file mode 100644 index 8e1859c72..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.gif +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go b/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go deleted file mode 100644 index c14bb4413..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/dashboard.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +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 deleted file mode 100644 index b7033580f..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import "github.com/gizak/termui" - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - g0 := termui.NewGauge() - g0.Percent = 40 - g0.Width = 50 - g0.Height = 3 - g0.Border.Label = "Slim Gauge" - g0.BarColor = termui.ColorRed - g0.Border.FgColor = termui.ColorWhite - g0.Border.LabelFgColor = termui.ColorCyan - - g2 := termui.NewGauge() - g2.Percent = 60 - g2.Width = 50 - g2.Height = 3 - g2.PercentColor = termui.ColorBlue - g2.Y = 3 - g2.Border.Label = "Slim Gauge" - g2.BarColor = termui.ColorYellow - g2.Border.FgColor = termui.ColorWhite - - g1 := termui.NewGauge() - g1.Percent = 30 - g1.Width = 50 - g1.Height = 5 - g1.Y = 6 - g1.Border.Label = "Big Gauge" - g1.PercentColor = termui.ColorYellow - g1.BarColor = termui.ColorGreen - g1.Border.FgColor = termui.ColorWhite - g1.Border.LabelFgColor = termui.ColorMagenta - - g3 := termui.NewGauge() - g3.Percent = 50 - g3.Width = 50 - g3.Height = 3 - g3.Y = 11 - g3.Border.Label = "Gauge with custom label" - g3.Label = "{{percent}}% (100MBs free)" - g3.LabelAlign = termui.AlignRight - - termui.Render(g0, g1, g2, g3) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png b/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png Binary files differdeleted file mode 100644 index 5c20e6e8a..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/gauge.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif Binary files differdeleted file mode 100644 index 7490043de..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.gif +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go b/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go deleted file mode 100644 index 49121411f..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/grid.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +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 deleted file mode 100644 index 1db543496..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import ( - "math" - - "github.com/gizak/termui" -) - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - sinps := (func() []float64 { - n := 220 - ps := make([]float64, n) - for i := range ps { - ps[i] = 1 + math.Sin(float64(i)/5) - } - return ps - })() - - lc0 := termui.NewLineChart() - lc0.Border.Label = "braille-mode Line Chart" - lc0.Data = sinps - lc0.Width = 50 - lc0.Height = 12 - lc0.X = 0 - lc0.Y = 0 - lc0.AxesColor = termui.ColorWhite - lc0.LineColor = termui.ColorGreen | termui.AttrBold - - lc1 := termui.NewLineChart() - lc1.Border.Label = "dot-mode Line Chart" - lc1.Mode = "dot" - lc1.Data = sinps - lc1.Width = 26 - lc1.Height = 12 - lc1.X = 51 - lc1.DotStyle = '+' - lc1.AxesColor = termui.ColorWhite - lc1.LineColor = termui.ColorYellow | termui.AttrBold - - lc2 := termui.NewLineChart() - lc2.Border.Label = "dot-mode Line Chart" - lc2.Mode = "dot" - lc2.Data = sinps[4:] - lc2.Width = 77 - lc2.Height = 16 - lc2.X = 0 - lc2.Y = 12 - lc2.AxesColor = termui.ColorWhite - lc2.LineColor = termui.ColorCyan | termui.AttrBold - - termui.Render(lc0, lc1, lc2) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png Binary files differdeleted file mode 100644 index 655ef435f..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/linechart.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/list.go b/Godeps/_workspace/src/github.com/gizak/termui/example/list.go deleted file mode 100644 index d33a3616c..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/list.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import "github.com/gizak/termui" - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - strs := []string{ - "[0] github.com/gizak/termui", - "[1] 你好,世界", - "[2] こんにちは世界", - "[3] keyboard.go", - "[4] output.go", - "[5] random_out.go", - "[6] dashboard.go", - "[7] nsf/termbox-go"} - - ls := termui.NewList() - ls.Items = strs - ls.ItemFgColor = termui.ColorYellow - ls.Border.Label = "List" - ls.Height = 7 - ls.Width = 25 - ls.Y = 0 - - termui.Render(ls) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/list.png b/Godeps/_workspace/src/github.com/gizak/termui/example/list.png Binary files differdeleted file mode 100644 index 8ca08c079..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/list.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go deleted file mode 100644 index a32a28e0a..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import "github.com/gizak/termui" - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - bc := termui.NewMBarChart() - math := []int{90, 85, 90, 80} - english := []int{70, 85, 75, 60} - science := []int{75, 60, 80, 85} - compsci := []int{100, 100, 100, 100} - bc.Data[0] = math - bc.Data[1] = english - bc.Data[2] = science - bc.Data[3] = compsci - studentsName := []string{"Ken", "Rob", "Dennis", "Linus"} - bc.Border.Label = "Student's Marks X-Axis=Name Y-Axis=Marks[Math,English,Science,ComputerScience] in %" - bc.Width = 100 - bc.Height = 50 - bc.Y = 10 - bc.BarWidth = 10 - bc.DataLabels = studentsName - bc.ShowScale = true //Show y_axis scale value (min and max) - bc.SetMax(400) - - bc.TextColor = termui.ColorGreen //this is color for label (x-axis) - bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience - bc.BarColor[1] = termui.ColorYellow //Bar Color for english - bc.NumColor[3] = termui.ColorRed // Num color for computerscience - bc.NumColor[1] = termui.ColorRed // num color for english - - //Other colors are automatically populated, btw All the students seems do well in computerscience. :p - - termui.Render(bc) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png b/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png Binary files differdeleted file mode 100644 index 9a4252616..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/mbarchart.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/par.go b/Godeps/_workspace/src/github.com/gizak/termui/example/par.go deleted file mode 100644 index ffbc60aa8..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/par.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import "github.com/gizak/termui" - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - par0 := termui.NewPar("Borderless Text") - par0.Height = 1 - par0.Width = 20 - par0.Y = 1 - par0.HasBorder = false - - par1 := termui.NewPar("你好,世界。") - par1.Height = 3 - par1.Width = 17 - par1.X = 20 - par1.Border.Label = "标签" - - par2 := termui.NewPar("Simple text\nwith label. It can be multilined with \\n or break automatically") - par2.Height = 5 - par2.Width = 37 - par2.Y = 4 - par2.Border.Label = "Multiline" - par2.Border.FgColor = termui.ColorYellow - - par3 := termui.NewPar("Long text with label and it is auto trimmed.") - par3.Height = 3 - par3.Width = 37 - par3.Y = 9 - par3.Border.Label = "Auto Trim" - - termui.Render(par0, par1, par2, par3) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/par.png b/Godeps/_workspace/src/github.com/gizak/termui/example/par.png Binary files differdeleted file mode 100644 index a85e64415..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/par.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go deleted file mode 100644 index f04baf570..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import "github.com/gizak/termui" - -func main() { - err := termui.Init() - if err != nil { - panic(err) - } - defer termui.Close() - - termui.UseTheme("helloworld") - - data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6} - spl0 := termui.NewSparkline() - spl0.Data = data[3:] - spl0.Title = "Sparkline 0" - spl0.LineColor = termui.ColorGreen - - // single - spls0 := termui.NewSparklines(spl0) - spls0.Height = 2 - spls0.Width = 20 - spls0.HasBorder = false - - spl1 := termui.NewSparkline() - spl1.Data = data - spl1.Title = "Sparkline 1" - spl1.LineColor = termui.ColorRed - - spl2 := termui.NewSparkline() - spl2.Data = data[5:] - spl2.Title = "Sparkline 2" - spl2.LineColor = termui.ColorMagenta - - // group - spls1 := termui.NewSparklines(spl0, spl1, spl2) - spls1.Height = 8 - spls1.Width = 20 - spls1.Y = 3 - spls1.Border.Label = "Group Sparklines" - - spl3 := termui.NewSparkline() - spl3.Data = data - spl3.Title = "Enlarged Sparkline" - spl3.Height = 8 - spl3.LineColor = termui.ColorYellow - - spls2 := termui.NewSparklines(spl3) - spls2.Height = 11 - spls2.Width = 30 - spls2.Border.FgColor = termui.ColorCyan - spls2.X = 21 - spls2.Border.Label = "Tweeked Sparkline" - - termui.Render(spls0, spls1, spls2) - - <-termui.EventCh() -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png b/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png Binary files differdeleted file mode 100644 index 9dd7c82b1..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/sparklines.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go b/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go deleted file mode 100644 index 30c51a343..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/theme.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -// +build ignore - -package main - -import ui "github.com/gizak/termui" -import "math" - -import "time" - -func main() { - err := ui.Init() - if err != nil { - panic(err) - } - defer ui.Close() - - ui.UseTheme("helloworld") - - p := ui.NewPar(":PRESS q TO QUIT DEMO") - p.Height = 3 - p.Width = 50 - p.Border.Label = "Text Box" - - strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"} - list := ui.NewList() - list.Items = strs - list.Border.Label = "List" - list.Height = 7 - list.Width = 25 - list.Y = 4 - - g := ui.NewGauge() - g.Percent = 50 - g.Width = 50 - g.Height = 3 - g.Y = 11 - g.Border.Label = "Gauge" - - spark := ui.NewSparkline() - spark.Title = "srv 0:" - spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6} - spark.Data = spdata - - spark1 := ui.NewSparkline() - spark1.Title = "srv 1:" - spark1.Data = spdata - - sp := ui.NewSparklines(spark, spark1) - sp.Width = 25 - sp.Height = 7 - sp.Border.Label = "Sparkline" - sp.Y = 4 - sp.X = 25 - - lc := ui.NewLineChart() - sinps := (func() []float64 { - n := 100 - ps := make([]float64, n) - for i := range ps { - ps[i] = 1 + math.Sin(float64(i)/4) - } - return ps - })() - - lc.Border.Label = "Line Chart" - lc.Data = sinps - lc.Width = 50 - lc.Height = 11 - lc.X = 0 - lc.Y = 14 - lc.Mode = "dot" - - bc := ui.NewBarChart() - bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6} - bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} - bc.Border.Label = "Bar Chart" - bc.Width = 26 - bc.Height = 10 - bc.X = 51 - bc.Y = 0 - bc.DataLabels = bclabels - - lc1 := ui.NewLineChart() - lc1.Border.Label = "Line Chart" - rndwalk := (func() []float64 { - n := 150 - d := make([]float64, n) - for i := 1; i < n; i++ { - if i < 20 { - d[i] = d[i-1] + 0.01 - } - if i > 20 { - d[i] = d[i-1] - 0.05 - } - } - return d - })() - lc1.Data = rndwalk - lc1.Width = 26 - lc1.Height = 11 - lc1.X = 51 - lc1.Y = 14 - - p1 := ui.NewPar("Hey!\nI am a borderless block!") - p1.HasBorder = false - p1.Width = 26 - p1.Height = 2 - p1.X = 52 - p1.Y = 11 - - draw := func(t int) { - g.Percent = t % 101 - list.Items = strs[t%9:] - sp.Lines[0].Data = spdata[t%10:] - sp.Lines[1].Data = spdata[t/2%10:] - lc.Data = sinps[t/2:] - lc1.Data = rndwalk[t:] - bc.Data = bcdata[t/2%10:] - ui.Render(p, list, g, sp, lc, bc, lc1, p1) - } - - evt := ui.EventCh() - i := 0 - for { - select { - case e := <-evt: - if e.Type == ui.EventKey && e.Ch == 'q' { - return - } - default: - draw(i) - i++ - if i == 102 { - return - } - time.Sleep(time.Second / 2) - } - } -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png Binary files differdeleted file mode 100644 index 23b574f96..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/themedefault.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png b/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png Binary files differdeleted file mode 100644 index eaf4d9303..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/example/themehelloworld.png +++ /dev/null diff --git a/Godeps/_workspace/src/github.com/gizak/termui/gauge.go b/Godeps/_workspace/src/github.com/gizak/termui/gauge.go index 986f4f3dc..244d2995e 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/gauge.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/gauge.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -21,33 +21,27 @@ import ( 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 -) +const ColorUndef Attribute = Attribute(^uint16(0)) type Gauge struct { Block - Percent int - BarColor Attribute - PercentColor Attribute - Label string - LabelAlign Align + 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: theme.GaugePercent, - BarColor: theme.GaugeBar, - Label: "{{percent}}%", - LabelAlign: AlignCenter, + Block: *NewBlock(), + PercentColor: ThemeAttr("gauge.percent.fg"), + BarColor: ThemeAttr("gauge.bar.bg"), + Label: "{{percent}}%", + LabelAlign: AlignCenter, + PercentColorHighlighted: ColorUndef, } g.Width = 12 @@ -56,28 +50,26 @@ func NewGauge() *Gauge { } // Buffer implements Bufferer interface. -func (g *Gauge) Buffer() []Point { - ps := g.Block.Buffer() +func (g *Gauge) Buffer() Buffer { + buf := g.Block.Buffer() // plot bar - w := g.Percent * g.innerWidth / 100 - for i := 0; i < g.innerHeight; i++ { + w := g.Percent * g.innerArea.Dx() / 100 + for i := 0; i < g.innerArea.Dy(); 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 + c := Cell{} + c.Ch = ' ' + c.Bg = g.BarColor + if c.Bg == ColorDefault { + c.Bg |= AttrReverse } - ps = append(ps, p) + 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.innerY + g.innerHeight/2 + pry := g.innerArea.Min.Y + g.innerArea.Dy()/2 rs := str2runes(s) var pos int switch g.LabelAlign { @@ -85,29 +77,33 @@ func (g *Gauge) Buffer() []Point { pos = 0 case AlignCenter: - pos = (g.innerWidth - strWidth(s)) / 2 + pos = (g.innerArea.Dx() - strWidth(s)) / 2 case AlignRight: - pos = g.innerWidth - strWidth(s) + pos = g.innerArea.Dx() - strWidth(s) - 1 } + pos += g.innerArea.Min.X 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 + 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 { - p.Bg = g.Block.BgColor + c.Bg = g.Block.Bg } - ps = append(ps, p) + buf.Set(1+pos+i, pry, c) } - return g.Block.chopOverflow(ps) + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/grid.go b/Godeps/_workspace/src/github.com/gizak/termui/grid.go index 5f6e85e76..364442e02 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/grid.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/grid.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -160,8 +160,8 @@ func (r *Row) SetWidth(w int) { // Buffer implements Bufferer interface, // recursively merge all widgets buffer -func (r *Row) Buffer() []Point { - merged := []Point{} +func (r *Row) Buffer() Buffer { + merged := NewBuffer() if r.isRenderableLeaf() { return r.Widget.Buffer() @@ -169,13 +169,13 @@ func (r *Row) Buffer() []Point { // for those are not leaves but have a renderable widget if r.Widget != nil { - merged = append(merged, r.Widget.Buffer()...) + merged.Merge(r.Widget.Buffer()) } // collect buffer from children if !r.isLeaf() { for _, c := range r.Cols { - merged = append(merged, c.Buffer()...) + merged.Merge(c.Buffer()) } } @@ -267,13 +267,13 @@ func (g *Grid) Align() { } // Buffer implments Bufferer interface. -func (g Grid) Buffer() []Point { - ps := []Point{} +func (g Grid) Buffer() Buffer { + buf := NewBuffer() + for _, r := range g.Rows { - ps = append(ps, r.Buffer()...) + buf.Merge(r.Buffer()) } - return ps + return buf } -// 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 deleted file mode 100644 index cdafb2052..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/grid_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -import ( - "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 index 80d8a0278..6690e7fb0 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/helper.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/helper.go @@ -1,10 +1,15 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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 tm "github.com/nsf/termbox-go" +import ( + "regexp" + "strings" + + tm "github.com/nsf/termbox-go" +) import rw "github.com/mattn/go-runewidth" /* ---------------Port from termbox-go --------------------- */ @@ -12,6 +17,7 @@ import rw "github.com/mattn/go-runewidth" // Attribute is printable cell's color and style. type Attribute uint16 +// 8 basic clolrs const ( ColorDefault Attribute = iota ColorBlack @@ -24,7 +30,10 @@ const ( ColorWhite ) -const NumberofColors = 8 //Have a constant that defines number of colors +//Have a constant that defines number of colors +const NumberofColors = 8 + +// Text style const ( AttrBold Attribute = 1 << (iota + 9) AttrUnderline @@ -46,15 +55,39 @@ 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) //[]rune(rw.Truncate(s, w, "")) + 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 { @@ -64,3 +97,118 @@ func strWidth(s string) int { 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 +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go b/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go deleted file mode 100644 index 6d1a56130..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/helper_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -import ( - "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/chart.go b/Godeps/_workspace/src/github.com/gizak/termui/linechart.go index d6fb8bc7d..f2829148e 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/chart.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/linechart.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -74,8 +74,8 @@ type LineChart struct { // NewLineChart returns a new LineChart with current theme. func NewLineChart() *LineChart { lc := &LineChart{Block: *NewBlock()} - lc.AxesColor = theme.LineChartAxes - lc.LineColor = theme.LineChartLine + lc.AxesColor = ThemeAttr("linechart.axes.fg") + lc.LineColor = ThemeAttr("linechart.line.fg") lc.Mode = "braille" lc.DotStyle = '•' lc.axisXLebelGap = 2 @@ -87,8 +87,8 @@ func NewLineChart() *LineChart { // one cell contains two data points // so the capicity is 2x as dot-mode -func (lc *LineChart) renderBraille() []Point { - ps := []Point{} +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? @@ -104,44 +104,48 @@ func (lc *LineChart) renderBraille() []Point { 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) + 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 { - 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) + 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 ps + return buf } -func (lc *LineChart) renderDot() []Point { - ps := []Point{} +func (lc *LineChart) renderDot() Buffer { + buf := NewBuffer() 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) + 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 ps + return buf } func (lc *LineChart) calcLabelX() { @@ -220,9 +224,9 @@ func (lc *LineChart) calcLayout() { lc.maxY = lc.Data[0] // valid visible range - vrange := lc.innerWidth + vrange := lc.innerArea.Dx() if lc.Mode == "braille" { - vrange = 2 * lc.innerWidth + vrange = 2 * lc.innerArea.Dx() } if vrange > len(lc.Data) { vrange = len(lc.Data) @@ -247,40 +251,30 @@ func (lc *LineChart) calcLayout() { lc.topValue = lc.maxY + 0.2*span } - lc.axisYHeight = lc.innerHeight - 2 + lc.axisYHeight = lc.innerArea.Dy() - 2 lc.calcLabelY() - lc.axisXWidth = lc.innerWidth - 1 - lc.labelYSpace + lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace lc.calcLabelX() - lc.drawingX = lc.innerX + 1 + lc.labelYSpace - lc.drawingY = lc.innerY + lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace + lc.drawingY = lc.innerArea.Min.Y } -func (lc *LineChart) plotAxes() []Point { - origY := lc.innerY + lc.innerHeight - 2 - origX := lc.innerX + lc.labelYSpace +func (lc *LineChart) plotAxes() Buffer { + buf := NewBuffer() + + origY := lc.innerArea.Min.Y + lc.innerArea.Dy() - 2 + origX := lc.innerArea.Min.X + lc.labelYSpace - ps := []Point{newPointWithAttrs(ORIGIN, origX, origY, lc.AxesColor, lc.BgColor)} + buf.Set(origX, origY, Cell{Ch: ORIGIN, Fg: lc.AxesColor, Bg: lc.Bg}) 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) + buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg}) } 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) + buf.Set(origX, origY-dy, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg}) } // x label @@ -290,13 +284,14 @@ func (lc *LineChart) plotAxes() []Point { 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) + 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.axisXLebelGap } @@ -304,33 +299,31 @@ func (lc *LineChart) plotAxes() []Point { // 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) + buf.Set( + lc.innerArea.Min.X+j, + origY-i*(lc.axisYLebelGap+1), + Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg}) } } - return ps + return buf } // Buffer implements Bufferer interface. -func (lc *LineChart) Buffer() []Point { - ps := lc.Block.Buffer() +func (lc *LineChart) Buffer() Buffer { + buf := lc.Block.Buffer() + if lc.Data == nil || len(lc.Data) == 0 { - return ps + return buf } lc.calcLayout() - ps = append(ps, lc.plotAxes()...) + buf.Merge(lc.plotAxes()) if lc.Mode == "dot" { - ps = append(ps, lc.renderDot()...) + buf.Merge(lc.renderDot()) } else { - ps = append(ps, lc.renderBraille()...) + buf.Merge(lc.renderBraille()) } - return lc.Block.chopOverflow(ps) + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go b/Godeps/_workspace/src/github.com/gizak/termui/linechart_others.go index 8911873b6..7e2e66b3e 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/chart_others.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/linechart_others.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. diff --git a/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go b/Godeps/_workspace/src/github.com/gizak/termui/linechart_windows.go index 9f9a5e96c..1478b5ce3 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/chart_windows.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/linechart_windows.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. diff --git a/Godeps/_workspace/src/github.com/gizak/termui/list.go b/Godeps/_workspace/src/github.com/gizak/termui/list.go index 0640932f2..670015fd3 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/list.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/list.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -41,64 +41,49 @@ type List struct { func NewList() *List { l := &List{Block: *NewBlock()} l.Overflow = "hidden" - l.ItemFgColor = theme.ListItemFg - l.ItemBgColor = theme.ListItemBg + l.ItemFgColor = ThemeAttr("list.item.fg") + l.ItemBgColor = ThemeAttr("list.item.bg") return l } // Buffer implements Bufferer interface. -func (l *List) Buffer() []Point { - ps := l.Block.Buffer() +func (l *List) Buffer() Buffer { + buf := l.Block.Buffer() + switch l.Overflow { case "wrap": - rs := str2runes(strings.Join(l.Items, "\n")) + cs := DefaultTxBuilder.Build(strings.Join(l.Items, "\n"), l.ItemFgColor, l.ItemBgColor) 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 { + 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 rs[k] == '\n' { + if cs[k].Ch == '\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 + buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, cs[k]) - ps = append(ps, pi) k++ j++ } case "hidden": trimItems := l.Items - if len(trimItems) > l.innerHeight { - trimItems = trimItems[:l.innerHeight] + if len(trimItems) > l.innerArea.Dy() { + trimItems = trimItems[:l.innerArea.Dy()] } for i, v := range trimItems { - rs := trimStr2Runes(v, l.innerWidth) - + cs := DTrimTxCls(DefaultTxBuilder.Build(v, l.ItemFgColor, l.ItemBgColor), l.innerArea.Dx()) 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) + for _, vv := range cs { + w := vv.Width() + buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, vv) j += w } } } - return l.Block.chopOverflow(ps) + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/mbar.go b/Godeps/_workspace/src/github.com/gizak/termui/mbarchart.go index 9d18c2cb4..231de277f 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/mbar.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/mbarchart.go @@ -1,4 +1,4 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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. @@ -48,16 +48,16 @@ type MBarChart struct { // 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.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.innerWidth / (bc.BarGap + bc.BarWidth) + bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth) bc.labels = make([][]rune, bc.numBar) DataLen := 0 LabelLen := len(bc.DataLabels) @@ -129,9 +129,9 @@ func (bc *MBarChart) layout() { if bc.ShowScale { s := fmt.Sprintf("%d", bc.max) bc.maxScale = trimStr2Runes(s, len(s)) - bc.scale = float64(bc.max) / float64(bc.innerHeight-2) + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2) } else { - bc.scale = float64(bc.max) / float64(bc.innerHeight-1) + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1) } } @@ -144,8 +144,8 @@ func (bc *MBarChart) SetMax(max int) { } // Buffer implements Bufferer interface. -func (bc *MBarChart) Buffer() []Point { - ps := bc.Block.Buffer() +func (bc *MBarChart) Buffer() Buffer { + buf := bc.Block.Buffer() bc.layout() var oftX int @@ -157,15 +157,17 @@ func (bc *MBarChart) Buffer() []Point { // plot bars for j := 0; j < bc.BarWidth; j++ { for k := 0; k < h; k++ { - p := Point{} - p.Ch = ' ' - p.Bg = bc.BarColor[i1] + c := Cell{ + Ch: ' ', + Bg: bc.BarColor[i1], + } if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent! - p.Bg |= AttrReverse + c.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) + 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 @@ -173,13 +175,14 @@ func (bc *MBarChart) Buffer() []Point { // 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) + 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 @@ -187,19 +190,20 @@ func (bc *MBarChart) Buffer() []Point { 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] + 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 - p.Bg |= AttrReverse + c.Bg |= AttrReverse } if h == 0 { - p.Bg = bc.BgColor + c.Bg = bc.Bg } - 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) + 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 } @@ -208,26 +212,31 @@ func (bc *MBarChart) Buffer() []Point { 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) + 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++ { - 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) + 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 bc.Block.chopOverflow(ps) + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/p.go b/Godeps/_workspace/src/github.com/gizak/termui/p.go deleted file mode 100644 index e327d7489..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/p.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -// 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/par.go b/Godeps/_workspace/src/github.com/gizak/termui/par.go new file mode 100644 index 000000000..c01bd0020 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/par.go @@ -0,0 +1,64 @@ +// 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.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: ThemeAttr("par.text.fg"), + TextBgColor: ThemeAttr("par.text.bg"), + } +} + +// 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) + + 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/Godeps/_workspace/src/github.com/gizak/termui/point.go b/Godeps/_workspace/src/github.com/gizak/termui/point.go deleted file mode 100644 index c381af9a4..000000000 --- a/Godeps/_workspace/src/github.com/gizak/termui/point.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. -// Use of this source code is governed by a MIT license that can -// be found in the LICENSE file. - -package termui - -// Point stands for a single cell in terminal. -type Point struct { - Ch rune - Bg Attribute - Fg Attribute - X int - Y int -} - -func newPoint(c rune, x, y int) (p Point) { - p.Ch = c - p.X = x - p.Y = y - return -} - -func newPointWithAttrs(c rune, x, y int, fg, bg Attribute) Point { - p := newPoint(c, x, y) - p.Bg = bg - p.Fg = fg - return p -} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/pos.go b/Godeps/_workspace/src/github.com/gizak/termui/pos.go new file mode 100644 index 000000000..2046dce5a --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/gizak/termui/render.go b/Godeps/_workspace/src/github.com/gizak/termui/render.go index d697d0aea..36544f063 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/render.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/render.go @@ -1,29 +1,62 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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 tm "github.com/nsf/termbox-go" +import ( + "image" + "sync" + "time" + + tm "github.com/nsf/termbox-go" +) // Bufferer should be implemented by all renderable components. type Bufferer interface { - Buffer() []Point + Buffer() Buffer } // Init initializes termui library. This function should be called before any others. // 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 = theme.BodyBg - defer func() { - w, _ := tm.Size() - Body.Width = w - evtListen() + 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 tm.Init() + + return nil } // Close finalizes termui library, @@ -32,29 +65,71 @@ 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 { - tm.Sync() - w, _ := tm.Size() - return w + termSync() + return termWidth } // TermHeight returns the current terminal's height. func TermHeight() int { - tm.Sync() - _, h := tm.Size() - return h + termSync() + return termHeight } // 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)) +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/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go b/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go index c63a5857f..312ad9563 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/sparkline.go @@ -1,12 +1,10 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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 "math" - -// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃ +// 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() @@ -49,8 +47,8 @@ func (s *Sparklines) Add(sl Sparkline) { func NewSparkline() Sparkline { return Sparkline{ Height: 1, - TitleColor: theme.SparklineTitle, - LineColor: theme.SparklineLine} + 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. @@ -67,13 +65,13 @@ func (sl *Sparklines) update() { sl.Lines[i].displayHeight = v.Height + 1 } } - sl.displayWidth = sl.innerWidth + 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.innerHeight { + if h+v.displayHeight <= sl.innerArea.Dy() { sl.displayLines++ } else { break @@ -84,20 +82,24 @@ func (sl *Sparklines) update() { for i := 0; i < sl.displayLines; i++ { data := sl.Lines[i].Data - max := math.MinInt32 + max := 0 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) + 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() []Point { - ps := sl.Block.Buffer() +func (sl *Sparklines) Buffer() Buffer { + buf := sl.Block.Buffer() sl.update() oftY := 0 @@ -105,52 +107,61 @@ func (sl *Sparklines) Buffer() []Point { l := sl.Lines[i] data := l.Data - if len(data) > sl.innerWidth { - data = data[len(data)-sl.innerWidth:] + if len(data) > sl.innerArea.Dx() { + data = data[len(data)-sl.innerArea.Dx():] } if l.Title != "" { - rs := trimStr2Runes(l.Title, sl.innerWidth) + rs := trimStr2Runes(l.Title, sl.innerArea.Dx()) 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) + 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++ { - p := Point{} - p.X = sl.innerX + j - p.Y = sl.innerY + oftY + l.Height - jj - p.Ch = ' ' // => sparks[7] - p.Bg = l.LineColor + 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 - ps = append(ps, p) + buf.Set(x, y, c) } 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) + 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 sl.Block.chopOverflow(ps) + return buf } diff --git a/Godeps/_workspace/src/github.com/gizak/termui/test/runtest.go b/Godeps/_workspace/src/github.com/gizak/termui/test/runtest.go new file mode 100644 index 000000000..99794c4db --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/test/runtest.go @@ -0,0 +1,66 @@ +// 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 main + +import ( + "fmt" + "os" + + "github.com/gizak/termui" + "github.com/gizak/termui/debug" +) + +func main() { + // run as client + if len(os.Args) > 1 { + fmt.Print(debug.ConnectAndListen()) + return + } + + // run as server + go func() { panic(debug.ListenAndServe()) }() + + if err := termui.Init(); err != nil { + panic(err) + } + defer termui.Close() + + //termui.UseTheme("helloworld") + b := termui.NewBlock() + b.Width = 20 + b.Height = 20 + b.Float = termui.AlignCenter + b.BorderLabel = "[HELLO](fg-red,bg-white) [WORLD](fg-blue,bg-green)" + + termui.Render(b) + + termui.Handle("/sys", func(e termui.Event) { + k, ok := e.Data.(termui.EvtKbd) + debug.Logf("->%v\n", e) + if ok && k.KeyStr == "q" { + termui.StopLoop() + } + }) + + termui.Handle(("/usr"), func(e termui.Event) { + debug.Logf("->%v\n", e) + }) + + termui.Handle("/timer/1s", func(e termui.Event) { + t := e.Data.(termui.EvtTimer) + termui.SendCustomEvt("/usr/t", t.Count) + + if t.Count%2 == 0 { + b.BorderLabel = "[HELLO](fg-red,bg-green) [WORLD](fg-blue,bg-white)" + } else { + b.BorderLabel = "[HELLO](fg-blue,bg-white) [WORLD](fg-red,bg-green)" + } + + termui.Render(b) + + }) + + termui.Loop() +} diff --git a/Godeps/_workspace/src/github.com/gizak/termui/textbuilder.go b/Godeps/_workspace/src/github.com/gizak/termui/textbuilder.go new file mode 100644 index 000000000..6ff6d21f3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/gizak/termui/textbuilder.go @@ -0,0 +1,215 @@ +// 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" +) + +// TextBuilder is a minial interface to produce text []Cell using sepcific 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 +} + +// 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/Godeps/_workspace/src/github.com/gizak/termui/theme.go b/Godeps/_workspace/src/github.com/gizak/termui/theme.go index c8ad94756..c3ccda559 100644 --- a/Godeps/_workspace/src/github.com/gizak/termui/theme.go +++ b/Godeps/_workspace/src/github.com/gizak/termui/theme.go @@ -1,9 +1,12 @@ -// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved. +// 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 @@ -29,6 +32,7 @@ type ColorScheme struct { MBarChartBar Attribute MBarChartText Attribute MBarChartNum Attribute + TabActiveBg Attribute } // default color scheme depends on the user's terminal setting. @@ -58,6 +62,7 @@ var themeHelloWorld = ColorScheme{ MBarChartBar: ColorRed, MBarChartNum: ColorWhite, MBarChartText: ColorCyan, + TabActiveBg: ColorMagenta, } var theme = themeDefault // global dep @@ -82,3 +87,54 @@ func UseTheme(th string) { 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/Godeps/_workspace/src/github.com/gizak/termui/widget.go b/Godeps/_workspace/src/github.com/gizak/termui/widget.go new file mode 100644 index 000000000..35cf143a3 --- /dev/null +++ b/Godeps/_workspace/src/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) +} |