diff --git a/README.md b/README.md
index 8144e49..233f94e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# termui
-
+[](./_examples/demo.go)
termui is a cross-platform and fully-customizable terminal dashboard and widget library built on top of [termbox-go](https://github.com/nsf/termbox-go). It is inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib) and written purely in Go.
@@ -15,58 +15,23 @@ go get -u github.com/gizak/termui@master
**Note**: termui is currently undergoing API changes so make sure to check the changelog when upgrading.
If you upgrade and notice something is missing or don't like a change, revert the upgrade and open an issue.
-## Usage
+## Widgets
-### Hello World
+- [BarChart](./_examples/barchart.go)
+- [Canvas](./_examples/canvas.go)
+- [Gauge](./_examples/gauge.go)
+- [LineChart](./_examples/linechart.go)
+- [List](./_examples/list.go)
+- [Paragraph](./_examples/paragraph.go)
+- [PieChart](./_examples/piechart.go)
+- [Sparkline](./_examples/sparkline.go)
+- [StackedBarChart](./_examples/stacked_barchart.go)
+- [Table](./_examples/table.go)
+- [Tabs](./_examples/tabs.go)
-```go
-package main
+Run an example with `go run _examples/{example}.go` or run all of them consecutively with `make run-examples`.
-import (
- "log"
-
- ui "github.com/gizak/termui"
- "github.com/gizak/termui/widgets"
-)
-
-func main() {
- if err := ui.Init(); err != nil {
- log.Fatalf("failed to initialize termui: %v", err)
- }
- defer ui.Close()
-
- p := widgets.NewParagraph()
- p.Text = "Hello World!"
- p.SetRect(0, 0, 25, 5)
-
- ui.Render(p)
-
- for e := range ui.PollEvents() {
- if e.Type == ui.KeyboardEvent {
- break
- }
- }
-}
-```
-
-### Widgets
-
-Click image to see the corresponding demo codes.
-
-[](https://github.com/gizak/termui/blob/master/_examples/barchart.go)
-[](https://github.com/gizak/termui/blob/master/_examples/gauge.go)
-[](https://github.com/gizak/termui/blob/master/_examples/linechart.go)
-[](https://github.com/gizak/termui/blob/master/_examples/list.go)
-[](https://github.com/gizak/termui/blob/master/_examples/paragraph.go)
-[](https://github.com/gizak/termui/blob/master/_examples/sparkline.go)
-[](https://github.com/gizak/termui/blob/master/_examples/stacked_barchart.go)
-[](https://github.com/gizak/termui/blob/master/_examples/table.go)
-
-### Examples
-
-Examples can be found in [\_examples](./_examples). Run an example with `go run _examples/{example}.go` or run all of them consecutively with `make run-examples`.
-
-### Documentation
+## Documentation
- [wiki](https://github.com/gizak/termui/wiki)
diff --git a/_assets/barchart.png b/_assets/barchart.png
deleted file mode 100644
index 4c5f1ca..0000000
Binary files a/_assets/barchart.png and /dev/null differ
diff --git a/_assets/dashboard1.gif b/_assets/demo.gif
similarity index 100%
rename from _assets/dashboard1.gif
rename to _assets/demo.gif
diff --git a/_assets/gauge.png b/_assets/gauge.png
deleted file mode 100644
index 4009243..0000000
Binary files a/_assets/gauge.png and /dev/null differ
diff --git a/_assets/grid.gif b/_assets/grid.gif
deleted file mode 100644
index 7490043..0000000
Binary files a/_assets/grid.gif and /dev/null differ
diff --git a/_assets/linechart.png b/_assets/linechart.png
deleted file mode 100644
index b0d0057..0000000
Binary files a/_assets/linechart.png and /dev/null differ
diff --git a/_assets/list.png b/_assets/list.png
deleted file mode 100644
index 7c5e27c..0000000
Binary files a/_assets/list.png and /dev/null differ
diff --git a/_assets/paragraph.png b/_assets/paragraph.png
deleted file mode 100644
index b71fa7c..0000000
Binary files a/_assets/paragraph.png and /dev/null differ
diff --git a/_assets/sparkline.png b/_assets/sparkline.png
deleted file mode 100644
index dd5641b..0000000
Binary files a/_assets/sparkline.png and /dev/null differ
diff --git a/_assets/stacked_barchart.png b/_assets/stacked_barchart.png
deleted file mode 100644
index e51337e..0000000
Binary files a/_assets/stacked_barchart.png and /dev/null differ
diff --git a/_assets/table.png b/_assets/table.png
deleted file mode 100644
index 5781b1d..0000000
Binary files a/_assets/table.png and /dev/null differ
diff --git a/_examples/dashboard2.go b/_examples/dashboard2.go
deleted file mode 100644
index 75c4ddf..0000000
--- a/_examples/dashboard2.go
+++ /dev/null
@@ -1,370 +0,0 @@
-// Copyright 2017 Zack Guo . 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 (
- "bufio"
- "errors"
- "fmt"
- "image"
- "io"
- "log"
- "os"
- "regexp"
- "runtime"
- "sort"
- "strconv"
- "strings"
- "time"
-
- ui "github.com/gizak/termui"
- "github.com/gizak/termui/widgets"
-)
-
-const statFilePath = "/proc/stat"
-const meminfoFilePath = "/proc/meminfo"
-
-type CpuStat struct {
- user float32
- nice float32
- system float32
- idle float32
-}
-
-type CpusStats struct {
- stat map[string]CpuStat
- proc map[string]CpuStat
-}
-
-func NewCpusStats(s map[string]CpuStat) *CpusStats {
- return &CpusStats{stat: s, proc: make(map[string]CpuStat)}
-}
-
-func (cs *CpusStats) String() (ret string) {
- for key, _ := range cs.proc {
- ret += fmt.Sprintf("%s: %.2f %.2f %.2f %.2f\n", key, cs.proc[key].user, cs.proc[key].nice, cs.proc[key].system, cs.proc[key].idle)
- }
- return
-}
-
-func subCpuStat(m CpuStat, s CpuStat) CpuStat {
- return CpuStat{user: m.user - s.user,
- nice: m.nice - s.nice,
- system: m.system - s.system,
- idle: m.idle - s.idle}
-}
-
-func procCpuStat(c CpuStat) CpuStat {
- sum := c.user + c.nice + c.system + c.idle
- return CpuStat{user: c.user / sum * 100,
- nice: c.nice / sum * 100,
- system: c.system / sum * 100,
- idle: c.idle / sum * 100}
-}
-
-func (cs *CpusStats) tick(ns map[string]CpuStat) {
- for key, _ := range cs.stat {
- proc := subCpuStat(ns[key], cs.stat[key])
- cs.proc[key] = procCpuStat(proc)
- cs.stat[key] = ns[key]
- }
-}
-
-type errIntParser struct {
- err error
-}
-
-func (eip *errIntParser) parse(s string) (ret int64) {
- if eip.err != nil {
- return 0
- }
- ret, eip.err = strconv.ParseInt(s, 10, 0)
- return
-}
-
-type LineProcessor interface {
- process(string) error
- finalize() interface{}
-}
-
-type CpuLineProcessor struct {
- m map[string]CpuStat
-}
-
-func (clp *CpuLineProcessor) process(line string) (err error) {
- r := regexp.MustCompile("^cpu([0-9]*)")
-
- if r.MatchString(line) {
- tab := strings.Fields(line)
- if len(tab) < 5 {
- err = errors.New("cpu info line has not enough fields")
- return
- }
- parser := errIntParser{}
- cs := CpuStat{user: float32(parser.parse(tab[1])),
- nice: float32(parser.parse(tab[2])),
- system: float32(parser.parse(tab[3])),
- idle: float32(parser.parse(tab[4]))}
- clp.m[tab[0]] = cs
- err = parser.err
- if err != nil {
- return
- }
- }
- return
-}
-
-func (clp *CpuLineProcessor) finalize() interface{} {
- return clp.m
-}
-
-type MemStat struct {
- total int64
- free int64
-}
-
-func (ms MemStat) String() (ret string) {
- ret = fmt.Sprintf("TotalMem: %d, FreeMem: %d\n", ms.total, ms.free)
- return
-}
-
-func (ms *MemStat) process(line string) (err error) {
- rtotal := regexp.MustCompile("^MemTotal:")
- rfree := regexp.MustCompile("^MemFree:")
- var aux int64
- if rtotal.MatchString(line) || rfree.MatchString(line) {
- tab := strings.Fields(line)
- if len(tab) < 3 {
- err = errors.New("mem info line has not enough fields")
- return
- }
- aux, err = strconv.ParseInt(tab[1], 10, 0)
- }
- if err != nil {
- return
- }
-
- if rtotal.MatchString(line) {
- ms.total = aux
- }
- if rfree.MatchString(line) {
- ms.free = aux
- }
- return
-}
-
-func (ms *MemStat) finalize() interface{} {
- return *ms
-}
-
-func processFileLines(filePath string, lp LineProcessor) (ret interface{}, err error) {
- var statFile *os.File
- statFile, err = os.Open(filePath)
- if err != nil {
- fmt.Printf("open: %v\n", err)
- }
- defer statFile.Close()
-
- statFileReader := bufio.NewReader(statFile)
-
- for {
- var line string
- line, err = statFileReader.ReadString('\n')
- if err == io.EOF {
- err = nil
- break
- }
- if err != nil {
- fmt.Printf("open: %v\n", err)
- break
- }
- line = strings.TrimSpace(line)
-
- err = lp.process(line)
- }
-
- ret = lp.finalize()
- return
-}
-
-func getCpusStatsMap() (m map[string]CpuStat, err error) {
- var aux interface{}
- aux, err = processFileLines(statFilePath, &CpuLineProcessor{m: make(map[string]CpuStat)})
- return aux.(map[string]CpuStat), err
-}
-
-func getMemStats() (ms MemStat, err error) {
- var aux interface{}
- aux, err = processFileLines(meminfoFilePath, &MemStat{})
- return aux.(MemStat), err
-}
-
-type CpuTabElems struct {
- GMap map[string]*widgets.Gauge
- LChart *widgets.LineChart
-}
-
-func NewCpuTabElems(width int) *CpuTabElems {
- lc := widgets.NewLineChart()
- lc.SetRect(0, 0, width, 12)
- lc.LineType = widgets.DotLine
- lc.Title = "CPU"
- return &CpuTabElems{
- GMap: make(map[string]*widgets.Gauge),
- LChart: lc,
- }
-}
-
-func (cte *CpuTabElems) AddGauge(key string, Y int, width int) *widgets.Gauge {
- cte.GMap[key] = widgets.NewGauge()
- cte.GMap[key].SetRect(0, Y, width, Y+3)
- cte.GMap[key].Title = key
- cte.GMap[key].Percent = 0 //int(val.user + val.nice + val.system)
- return cte.GMap[key]
-}
-
-func (cte *CpuTabElems) Update(cs CpusStats) {
- for key, val := range cs.proc {
- p := int(val.user + val.nice + val.system)
- cte.GMap[key].Percent = p
- if key == "cpu" {
- cte.LChart.Data = append(cte.LChart.Data, []float64{})
- cte.LChart.Data[0] = append(cte.LChart.Data[0], 0)
- copy(cte.LChart.Data[0][1:], cte.LChart.Data[0][0:])
- cte.LChart.Data[0][0] = float64(p)
- }
- }
-}
-
-type MemTabElems struct {
- Gauge *widgets.Gauge
- SLines *widgets.SparklineGroup
-}
-
-func NewMemTabElems(width int) *MemTabElems {
- g := widgets.NewGauge()
- g.SetRect(0, 5, width, 10)
-
- sline := widgets.NewSparkline()
- sline.Title = "MEM"
-
- sls := widgets.NewSparklineGroup(sline)
- sls.SetRect(0, 10, width, 25)
- return &MemTabElems{Gauge: g, SLines: sls}
-}
-
-func (mte *MemTabElems) Update(ms MemStat) {
- used := (ms.total - ms.free) * 100 / ms.total
- mte.Gauge.Percent = int(used)
- mte.SLines.Sparklines[0].Data = append(mte.SLines.Sparklines[0].Data, 0)
- copy(mte.SLines.Sparklines[0].Data[1:], mte.SLines.Sparklines[0].Data[0:])
- mte.SLines.Sparklines[0].Data[0] = float64(used)
- if len(mte.SLines.Sparklines[0].Data) > mte.SLines.Dx()-2 {
- mte.SLines.Sparklines[0].Data = mte.SLines.Sparklines[0].Data[0 : mte.SLines.Dx()-2]
- }
-}
-
-func main() {
- if runtime.GOOS != "linux" {
- log.Fatalf("Currently only works on Linux")
- }
- if err := ui.Init(); err != nil {
- log.Fatalf("failed to initialize termui: %v", err)
- }
- defer ui.Close()
-
- termWidth := 70
-
- header := widgets.NewParagraph()
- header.Text = "Press q to quit, Press h or l to switch tabs"
- header.SetRect(0, 0, 50, 1)
- header.Border = false
- header.TextStyle.Bg = ui.ColorBlue
-
- cs, errcs := getCpusStatsMap()
- cpusStats := NewCpusStats(cs)
-
- if errcs != nil {
- panic("error")
- }
-
- cpuTabElems := NewCpuTabElems(termWidth)
-
- Y := 5
- cpuKeys := make([]string, 0, len(cs))
- for key := range cs {
- cpuKeys = append(cpuKeys, key)
- }
- sort.Strings(cpuKeys)
- for _, key := range cpuKeys {
- cpuTabElems.AddGauge(key, Y, termWidth)
- Y += 3
- }
- cpuTabElems.LChart.Rectangle = cpuTabElems.LChart.GetRect().Add(image.Pt(0, Y))
-
- memTabElems := NewMemTabElems(termWidth)
- ms, errm := getMemStats()
- if errm != nil {
- panic(errm)
- }
- memTabElems.Update(ms)
-
- tabpane := widgets.NewTabPane("CPU", "MEM")
- tabpane.SetRect(0, 1, 30, 30)
- tabpane.Border = false
- renderTab := func() {
- switch tabpane.ActiveTabIndex {
- case 0:
- ui.Render(cpuTabElems.LChart)
- for _, gauge := range cpuTabElems.GMap {
- ui.Render(gauge)
- }
- case 1:
- ui.Render(memTabElems.Gauge, memTabElems.SLines)
- }
- }
-
- ui.Render(header, tabpane)
- renderTab()
-
- tickerCount := 1
- uiEvents := ui.PollEvents()
- ticker := time.NewTicker(time.Second).C
- for {
- select {
- case e := <-uiEvents:
- switch e.ID {
- case "q", "":
- return
- case "h":
- tabpane.FocusLeft()
- ui.Render(header, tabpane)
- renderTab()
- case "l":
- tabpane.FocusRight()
- ui.Render(header, tabpane)
- renderTab()
- }
- case <-ticker:
- cs, errcs := getCpusStatsMap()
- if errcs != nil {
- panic(errcs)
- }
- cpusStats.tick(cs)
- cpuTabElems.Update(*cpusStats)
-
- ms, errm := getMemStats()
- if errm != nil {
- panic(errm)
- }
- memTabElems.Update(ms)
- ui.Render(header, tabpane)
- renderTab()
- tickerCount++
- }
- }
-}
diff --git a/_examples/dashboard1.go b/_examples/demo.go
similarity index 100%
rename from _examples/dashboard1.go
rename to _examples/demo.go
diff --git a/_examples/grid_nested.go b/_examples/grid_nested.go
deleted file mode 100644
index a219a4e..0000000
--- a/_examples/grid_nested.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// +build ignore
-
-package main
-
-import (
- "log"
-
- ui "github.com/gizak/termui"
-)
-
-func main() {
- if err := ui.Init(); err != nil {
- log.Fatalf("failed to initialize termui: %v", err)
- }
- defer ui.Close()
-
- grid := ui.NewGrid()
- termWidth, termHeight := ui.TerminalDimensions()
- grid.SetRect(0, 0, termWidth, termHeight)
-
- grid2 := ui.NewGrid()
-
- grid2.Set(
- ui.NewCol(.5, ui.NewBlock()),
- ui.NewCol(.5, ui.NewRow(.5, ui.NewBlock())),
- )
-
- grid.Set(
- ui.NewRow(.5, ui.NewBlock()),
- ui.NewRow(.5, grid2),
- )
-
- ui.Render(grid)
-
- uiEvents := ui.PollEvents()
- for {
- e := <-uiEvents
- switch e.ID {
- case "q", "":
- return
- case "":
- payload := e.Payload.(ui.Resize)
- grid.SetRect(0, 0, payload.Width, payload.Height)
- ui.Clear()
- ui.Render(grid)
- }
- }
-}