This commit is contained in:
gizak 2015-03-24 17:16:43 -04:00
parent b689689056
commit b8d37842ee
12 changed files with 197 additions and 24 deletions

16
bar.go
View File

@ -6,6 +6,20 @@ package termui
import "fmt"
// BarChart creates multiple bars in a widget:
/*
bc := termui.NewBarChart()
data := []int{3, 2, 5, 3, 9, 5}
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
bc.Border.Label = "Bar Chart"
bc.Data = data
bc.Width = 26
bc.Height = 10
bc.DataLabels = bclabels
bc.TextColor = termui.ColorGreen
bc.BarColor = termui.ColorRed
bc.NumColor = termui.ColorYellow
*/
type BarChart struct {
Block
BarColor Attribute
@ -22,6 +36,7 @@ type BarChart struct {
max int
}
// NewBarChart returns a new *BarChart with current theme.
func NewBarChart() *BarChart {
bc := &BarChart{Block: *NewBlock()}
bc.BarColor = theme.BarChartBar
@ -53,6 +68,7 @@ func (bc *BarChart) layout() {
bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
}
// Buffer implements Bufferer interface.
func (bc *BarChart) Buffer() []Point {
ps := bc.Block.Buffer()
bc.layout()

View File

@ -4,7 +4,9 @@
package termui
// basic struct, consider it as css: display:block
// Block is a base struct for all other upper level widgets,
// consider it as css: display:block.
// Normally you do not need to create it manually.
type Block struct {
X int
Y int
@ -24,6 +26,7 @@ type Block struct {
PaddingRight int
}
// NewBlock returns a *Block which inherits styles from current theme.
func NewBlock() *Block {
d := Block{}
d.IsDisplay = true
@ -52,11 +55,13 @@ func (d *Block) align() {
d.Border.Y = d.Y
d.Border.Width = d.Width
d.Border.Height = d.Height
d.innerX += 1
d.innerY += 1
d.innerX++
d.innerY++
}
}
// Buffer implements Bufferer interface.
// Draw background and border (if any).
func (d *Block) Buffer() []Point {
d.align()
@ -82,22 +87,23 @@ func (d *Block) Buffer() []Point {
return ps
}
// GetHeight implements GridBufferer.
// It returns current height of the block.
func (d Block) GetHeight() int {
return d.Height
}
func (d Block) GetWidth() int {
return d.Width
}
// SetX implements GridBufferer interface, which sets block's x position.
func (d *Block) SetX(x int) {
d.X = x
}
// SetY implements GridBufferer interface, it sets y position for block.
func (d *Block) SetY(y int) {
d.Y = y
}
// SetWidth implements GridBuffer interface, it sets block's width.
func (d *Block) SetWidth(w int) {
d.Width = w
}

4
box.go
View File

@ -29,6 +29,7 @@ type vline struct {
BgColor Attribute
}
// Draw a horizontal line.
func (l hline) Buffer() []Point {
pts := make([]Point, l.Length)
for i := 0; i < l.Length; i++ {
@ -41,6 +42,7 @@ func (l hline) Buffer() []Point {
return pts
}
// Draw a vertical line.
func (l vline) Buffer() []Point {
pts := make([]Point, l.Length)
for i := 0; i < l.Length; i++ {
@ -53,6 +55,7 @@ func (l vline) Buffer() []Point {
return pts
}
// Draw a box border.
func (b border) Buffer() []Point {
if b.Width < 2 || b.Height < 2 {
return nil
@ -98,6 +101,7 @@ type labeledBorder struct {
LabelBgColor Attribute
}
// Draw a box border with label.
func (lb labeledBorder) Buffer() []Point {
ps := lb.border.Buffer()
maxTxtW := lb.Width - 2

View File

@ -32,8 +32,18 @@ var braillePatterns = map[[2]int]rune{
var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
//var singleBraille = [4]rune{'⣀', '⠤', '⠒', '⠉'}
// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode,
// because one braille char can represent two data points.
/*
lc := termui.NewLineChart()
lc.Border.Label = "braille-mode Line Chart"
lc.Data = [1.2, 1.3, 1.5, 1.7, 1.5, 1.6, 1.8, 2.0]
lc.Width = 50
lc.Height = 12
lc.AxesColor = termui.ColorWhite
lc.LineColor = termui.ColorGreen | termui.AttrBold
// termui.Render(lc)...
*/
type LineChart struct {
Block
Data []float64
@ -58,6 +68,7 @@ type LineChart struct {
minY float64
}
// NewLineChart returns a new LineChart with current theme.
func NewLineChart() *LineChart {
lc := &LineChart{Block: *NewBlock()}
lc.AxesColor = theme.LineChartAxes
@ -298,6 +309,7 @@ func (lc *LineChart) plotAxes() []Point {
return ps
}
// Buffer implements Bufferer interface.
func (lc *LineChart) Buffer() []Point {
ps := lc.Block.Buffer()
if lc.Data == nil || len(lc.Data) == 0 {

27
doc.go Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
/*
Package termui is a library designed for creating command line UI. For more info, goto http://github.com/gizak/termui
A simplest example:
package main
import ui "github.com/gizak/termui"
func main() {
if err:=ui.Init(); err != nil {
panic(err)
}
defer ui.Close()
g := ui.NewGauge()
g.Percent = 50
g.Width = 50
g.Border.Label = "Gauge"
ui.Render(g)
}
*/
package termui

View File

@ -6,6 +6,17 @@ package termui
import "strconv"
// Gauge is a progress bar like widget.
// A simple example:
/*
g := termui.NewGauge()
g.Percent = 40
g.Width = 50
g.Height = 3
g.Border.Label = "Slim Gauge"
g.BarColor = termui.ColorRed
g.PercentColor = termui.ColorBlue
*/
type Gauge struct {
Block
Percent int
@ -13,6 +24,7 @@ type Gauge struct {
PercentColor Attribute
}
// NewGauge return a new gauge with current theme.
func NewGauge() *Gauge {
g := &Gauge{
Block: *NewBlock(),
@ -23,6 +35,7 @@ func NewGauge() *Gauge {
return g
}
// Buffer implements Bufferer interface.
func (g *Gauge) Buffer() []Point {
ps := g.Block.Buffer()

60
grid.go
View File

@ -4,8 +4,8 @@
package termui
// Bufferers that can be manipulated by Grid
type LayoutBufferer interface {
// GridBufferer introduces a Bufferer that can be manipulated by Grid.
type GridBufferer interface {
Bufferer
GetHeight() int
SetWidth(int)
@ -13,10 +13,10 @@ type LayoutBufferer interface {
SetY(int)
}
// build a layout tree
// row builds a layout tree
type row struct {
Cols []*row //children
Widget LayoutBufferer // root
Cols []*row //children
Widget GridBufferer // root
X int
Y int
Width int
@ -25,6 +25,7 @@ type row struct {
Offset int
}
// calculate and set the underlying layout tree's x, y, height and width.
func (r *row) calcLayout() {
r.assignWidth(r.Width)
r.Height = r.solveHeight()
@ -32,6 +33,7 @@ func (r *row) calcLayout() {
r.assignY(r.Y)
}
// tell if the node is leaf in the tree.
func (r *row) isLeaf() bool {
return r.Cols == nil || len(r.Cols) == 0
}
@ -40,16 +42,18 @@ func (r *row) isRenderableLeaf() bool {
return r.isLeaf() && r.Widget != nil
}
// assign widgets' (and their parent rows') width recursively.
func (r *row) assignWidth(w int) {
cw := int(float64(w*r.Span) / 12)
r.SetWidth(cw)
for i, _ := range r.Cols {
for i := range r.Cols {
r.Cols[i].assignWidth(cw)
}
}
// bottom up, return r's total height
// bottom up calc and set rows' (and their widgets') height,
// return r's total height.
func (r *row) solveHeight() int {
if r.isRenderableLeaf() {
r.Height = r.Widget.GetHeight()
@ -74,6 +78,7 @@ func (r *row) solveHeight() int {
return maxh
}
// recursively assign x position for r tree.
func (r *row) assignX(x int) {
r.SetX(x)
@ -89,6 +94,7 @@ func (r *row) assignX(x int) {
}
}
// recursively assign y position to r.
func (r *row) assignY(y int) {
r.SetY(y)
@ -106,10 +112,12 @@ func (r *row) assignY(y int) {
}
// GetHeight implements GridBufferer interface.
func (r row) GetHeight() int {
return r.Height
}
// SetX implements GridBufferer interface.
func (r *row) SetX(x int) {
r.X = x
if r.Widget != nil {
@ -117,6 +125,7 @@ func (r *row) SetX(x int) {
}
}
// SetY implements GridBufferer interface.
func (r *row) SetY(y int) {
r.Y = y
if r.Widget != nil {
@ -124,6 +133,7 @@ func (r *row) SetY(y int) {
}
}
// SetWidth implements GridBufferer interface.
func (r *row) SetWidth(w int) {
r.Width = w
if r.Widget != nil {
@ -131,6 +141,7 @@ func (r *row) SetWidth(w int) {
}
}
// Buffer implements Bufferer interface,
// recursively merge all widgets buffer
func (r *row) Buffer() []Point {
merged := []Point{}
@ -154,6 +165,27 @@ func (r *row) Buffer() []Point {
return merged
}
// Grid implements 12 columns system.
// A simple example:
/*
import ui "github.com/gizak/termui"
// init and create widgets...
// build
ui.Body.AddRows(
ui.NewRow(
ui.NewCol(6, 0, widget0),
ui.NewCol(6, 0, widget1)),
ui.NewRow(
ui.NewCol(3, 0, widget2),
ui.NewCol(3, 0, widget30, widget31, widget32),
ui.NewCol(6, 0, widget4)))
// calculate layout
ui.Body.Align()
ui.Render(ui.Body)
*/
type Grid struct {
Rows []*row
Width int
@ -162,23 +194,25 @@ type Grid struct {
BgColor Attribute
}
// NewGrid returns *Grid with given rows.
func NewGrid(rows ...*row) *Grid {
return &Grid{Rows: rows}
}
// AddRows appends given rows to Grid.
func (g *Grid) AddRows(rs ...*row) {
g.Rows = append(g.Rows, rs...)
}
// NewRow creates a new row out of given columns.
func NewRow(cols ...*row) *row {
rs := &row{Span: 12, Cols: cols}
return rs
}
// NewCol accepts: widgets are LayoutBufferer or
// widgets is A NewRow
// Note that if multiple widgets are provided, they will stack up in the col
func NewCol(span, offset int, widgets ...LayoutBufferer) *row {
// NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow.
// Note that if multiple widgets are provided, they will stack up in the col.
func NewCol(span, offset int, widgets ...GridBufferer) *row {
r := &row{Span: span, Offset: offset}
if widgets != nil && len(widgets) == 1 {
@ -203,7 +237,7 @@ func NewCol(span, offset int, widgets ...LayoutBufferer) *row {
return r
}
// Calculate each rows' layout
// Align calculate each rows' layout.
func (g *Grid) Align() {
h := 0
for _, r := range g.Rows {
@ -215,6 +249,7 @@ func (g *Grid) Align() {
}
}
// Buffer implments Bufferer interface.
func (g Grid) Buffer() []Point {
ps := []Point{}
for _, r := range g.Rows {
@ -223,4 +258,5 @@ func (g Grid) Buffer() []Point {
return ps
}
// Body corresponds to the entire terminal display region.
var Body *Grid

25
list.go
View File

@ -6,6 +6,29 @@ package termui
import "strings"
// List displays []string as its items,
// it has a Overflow option (default is "hidden"), when set to "hidden",
// the item exceeding List's width is truncated, but when set to "wrap",
// the overflowed text breaks into next line.
/*
strs := []string{
"[0] github.com/gizak/termui",
"[1] editbox.go",
"[2] iterrupt.go",
"[3] keyboard.go",
"[4] output.go",
"[5] random_out.go",
"[6] dashboard.go",
"[7] nsf/termbox-go"}
ls := termui.NewList()
ls.Items = strs
ls.ItemFgColor = termui.ColorYellow
ls.Border.Label = "List"
ls.Height = 7
ls.Width = 25
ls.Y = 0
*/
type List struct {
Block
Items []string
@ -14,6 +37,7 @@ type List struct {
ItemBgColor Attribute
}
// NewList returns a new *List with current theme.
func NewList() *List {
l := &List{Block: *NewBlock()}
l.Overflow = "hidden"
@ -22,6 +46,7 @@ func NewList() *List {
return l
}
// Buffer implements Bufferer interface.
func (l *List) Buffer() []Point {
ps := l.Block.Buffer()
switch l.Overflow {

9
p.go
View File

@ -4,6 +4,13 @@
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
@ -11,6 +18,7 @@ type Par struct {
TextBgColor Attribute
}
// NewPar returns a new *Par with given text as its content.
func NewPar(s string) *Par {
return &Par{
Block: *NewBlock(),
@ -19,6 +27,7 @@ func NewPar(s string) *Par {
TextBgColor: theme.ParTextBg}
}
// Buffer implements Bufferer interface.
func (p *Par) Buffer() []Point {
ps := p.Block.Buffer()

View File

@ -4,6 +4,7 @@
package termui
// Point stands for a single cell in terminal.
type Point struct {
Ch rune
Bg Attribute

View File

@ -6,11 +6,13 @@ package termui
import tm "github.com/nsf/termbox-go"
// all renderable components should implement this
// Bufferer should be implemented by all renderable components.
type Bufferer interface {
Buffer() []Point
}
// Init initializes termui library. This function should be called before any others.
// After initialization, the library must be finalized by 'Close' function.
func Init() error {
Body = NewGrid()
Body.X = 0
@ -23,21 +25,26 @@ func Init() error {
return tm.Init()
}
// Close finalizes termui library,
// should be called after successful initialization when termui's functionality isn't required anymore.
func Close() {
tm.Close()
}
// TermWidth returns the current terminal's width.
func TermWidth() int {
w, _ := tm.Size()
return w
}
// TermHeight returns the current terminal's height.
func TermHeight() int {
_, h := tm.Size()
return h
}
// render all from left to right, right could overlap on left ones
// 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 {

View File

@ -6,6 +6,14 @@ package termui
import "math"
// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃
/*
data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1}
spl := termui.NewSparkline()
spl.Data = data
spl.Title = "Sparkline 0"
spl.LineColor = termui.ColorGreen
*/
type Sparkline struct {
Data []int
Height int
@ -17,6 +25,12 @@ type Sparkline struct {
max int
}
// Sparklines is a renderable widget which groups together the given sparklines.
/*
spls := termui.NewSparklines(spl0,spl1,spl2) //...
spls.Height = 2
spls.Width = 20
*/
type Sparklines struct {
Block
Lines []Sparkline
@ -26,11 +40,12 @@ type Sparklines struct {
var sparks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
// Add appends a given Sparkline to s *Sparklines.
func (s *Sparklines) Add(sl Sparkline) {
s.Lines = append(s.Lines, sl)
}
// return unrenderable single sparkline, need to add it into Sparklines
// NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines.
func NewSparkline() Sparkline {
return Sparkline{
Height: 1,
@ -38,6 +53,7 @@ func NewSparkline() Sparkline {
LineColor: theme.SparklineLine}
}
// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later.
func NewSparklines(ss ...Sparkline) *Sparklines {
s := &Sparklines{Block: *NewBlock(), Lines: ss}
return s
@ -79,6 +95,7 @@ func (sl *Sparklines) update() {
}
}
// Buffer implements Bufferer interface.
func (sl *Sparklines) Buffer() []Point {
ps := sl.Block.Buffer()
sl.update()