Full Code of gizak/termui for AI

master 3ee54a07c7d1 cached
54 files
106.9 KB
36.2k tokens
296 symbols
1 requests
Download .txt
Repository: gizak/termui
Branch: master
Commit: 3ee54a07c7d1
Files: 54
Total size: 106.9 KB

Directory structure:
gitextract_vw2ezebp/

├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── _examples/
│   ├── barchart.go
│   ├── canvas.go
│   ├── demo.go
│   ├── gauge.go
│   ├── grid.go
│   ├── hello_world.go
│   ├── image.go
│   ├── list.go
│   ├── paragraph.go
│   ├── piechart.go
│   ├── plot.go
│   ├── sparkline.go
│   ├── stacked_barchart.go
│   ├── table.go
│   ├── tabs.go
│   └── tree.go
├── _scripts/
│   └── copyright_header.py
├── _test/
│   └── log_events.go
├── alignment.go
├── backend.go
├── block.go
├── buffer.go
├── canvas.go
├── doc.go
├── drawille/
│   └── drawille.go
├── events.go
├── go.mod
├── go.sum
├── grid.go
├── render.go
├── style.go
├── style_parser.go
├── symbols.go
├── symbols_other.go
├── symbols_windows.go
├── theme.go
├── utils.go
└── widgets/
    ├── barchart.go
    ├── gauge.go
    ├── image.go
    ├── list.go
    ├── paragraph.go
    ├── piechart.go
    ├── plot.go
    ├── sparkline.go
    ├── stacked_barchart.go
    ├── table.go
    ├── tabs.go
    └── tree.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
.vscode/
.mypy_cache/
.idea


================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [3.1.0] - 2019-07-15

### Added

- Added Tree widget [#237]

## [3.0.0] - 2019-03-07

### Changed

- Added sync.Locker interface to Drawable interface

## 2019-03-01

### Changed

- Change scroll method names in List widget

### Fixed

- Fix List widget scrolling

## 2019-02-28

### Added

- Add `ColumnResizer` to table which allows for custom column sizing
- Add widget padding

### Changed

- Change various widget field names
- s/`TextParse`/`ParseStyles`
- Remove `AddColorMap` in place of modifying `StyleParserColorMap` directly

## 2019-01-31

### Added

- Add more scrolling options to List

### Changed

- Make list scroll automatically

### Added

## 2019-01-26

### Added

- Add scrolling to List widget
- Add WrapText option to Paragraph
  - controls if text should wrap automatically

## 2019-01-24

### Added

- Add image widget [#126]

### Changed

- Change LineChart to Plot
  - Added ScatterPlot mode which plots points instead of lines between points

## 2019-01-23

### Added

- Add `Canvas` which allows for drawing braille lines to a `Buffer`

### Changed

- Set `termbox-go` backend to 256 colors by default
- Moved widgets to `github.com/gizak/termui/widgets`
- Rewrote widgets (check examples and code)
- Rewrote grid
  - grids are instantiated locally instead of through `termui.Body`
  - grids can be nested
  - change grid layout mechanism
    - columns and rows can be arbitrarily nested
    - column and row size is now specified as a ratio of the available space
- `Cell`s now contain a `Style` which holds a `Fg`, `Bg`, and `Modifier`
- Change `Bufferer` interface to `Drawable`
  - Add `GetRect` and `SetRect` methods to control widget sizing
  - Change `Buffer` method to `Draw`
    - `Draw` takes a `Buffer` and draws to it instead of returning a new `Buffer`
- Refactor `Theme`
  - `Theme` is now a large struct which holds the default `Styles` of everything
- Combine `TermWidth` and `TermHeight` functions into `TerminalDimensions`
- Rework `Block`
- Rework `Buffer` methods
- Decremente color numbers by 1 to match xterm colors
- Change text parsing
  - change style items from `fg-color` to `fg:color`
  - adde mod item like `mod:reverse`

## 2018-11-29

### Changed

- Move Tabpane from termui/extra to termui and rename it to TabPane
- Rename PollEvent to PollEvents

## 2018-11-28

### Changed

- Migrate from Dep to vgo
- Overhaul the event system
  - check the wiki/examples for details
- Rename Par widget to Paragraph
- Rename MBarChart widget to StackedBarChart

[#237]: https://github.com/gizak/termui/pull/237
[#126]: https://github.com/gizak/termui/pull/126

[Unreleased]: https://github.com/gizak/termui/compare/v3.1.0...HEAD
[3.1.0]: https://github.com/gizak/termui/compare/v3.0.0...v3.1.0
[3.0.0]: https://github.com/gizak/termui/compare/v2.3.0...v3.0.0


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Zack Guo

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: Makefile
================================================
.PHONY: run-examples
run-examples:
	@for file in _examples/*.go; do \
	  go run $$file; \
	  done;


================================================
FILE: README.md
================================================
# termui

[<img src="./_assets/demo.gif" alt="demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)" width="100%">](./_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 [tui-rs](https://github.com/fdehau/tui-rs) and written purely in Go.

## Note

Please be aware that due to my fluctuating availability, the frequency of updates to this project may not always follow a consistent schedule. I would like to invite potential maintainers to contribute to this project. If you are interested in becoming a maintainer, please do not hesitate to reach out to me.

## Versions

termui is currently compatible with Go 1.15 (as in go.mod) and above (tracking the Debian's [oldstable](https://wiki.debian.org/DebianReleases)). Please use the version-numbered branch as stable release. The new changes will be pushed to master branch first and then merge to version branch.

## Features

- Several premade widgets for common use cases
- Easily create custom widgets
- Position widgets either in a relative grid or with absolute coordinates
- Keyboard, mouse, and terminal resizing events
- Colors and styling

## Installation

### Go modules

It is not necessary to `go get` termui, since Go will automatically manage any imported dependencies for you. Do note that you have to include `/v3` in the import statements as shown in the 'Hello World' example below.

### Dep

Add with `dep ensure -add github.com/gizak/termui`. With Dep, `/v3` should *not* be included in the import statements.

## Hello World

```go
package main

import (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/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

- [BarChart](./_examples/barchart.go)
- [Canvas](./_examples/canvas.go) (for drawing braille dots)
- [Gauge](./_examples/gauge.go)
- [Image](./_examples/image.go)
- [List](./_examples/list.go)
- [Tree](./_examples/tree.go)
- [Paragraph](./_examples/paragraph.go)
- [PieChart](./_examples/piechart.go)
- [Plot](./_examples/plot.go) (for scatterplots and linecharts)
- [Sparkline](./_examples/sparkline.go)
- [StackedBarChart](./_examples/stacked_barchart.go)
- [Table](./_examples/table.go)
- [Tabs](./_examples/tabs.go)

Run an example with `go run _examples/{example}.go` or run each example consecutively with `make run-examples`.

## Documentation

- [wiki](https://github.com/gizak/termui/wiki)

## Uses

- [dockdash](https://github.com/byrnedo/dockdash)
- [expvarmon](https://github.com/divan/expvarmon)
- [go-ethereum/monitorcmd](https://github.com/ethereum/go-ethereum/blob/master/cmd/geth/monitorcmd.go)
- [go-jira-ui](https://github.com/mikepea/go-jira-ui)
- [gotop](https://github.com/cjbassi/gotop)
- [termeter](https://github.com/atsaki/termeter)
- [updo](https://github.com/Owloops/updo)

## Related Works

- [blessed-contrib](https://github.com/yaronn/blessed-contrib)
- [gocui](https://github.com/jroimartin/gocui)
- [termdash](https://github.com/mum4k/termdash)
- [tui-rs](https://github.com/fdehau/tui-rs)
- [tview](https://github.com/rivo/tview)

## License

[MIT](http://opensource.org/licenses/MIT)


================================================
FILE: _examples/barchart.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	bc := widgets.NewBarChart()
	bc.Data = []float64{3, 2, 5, 3, 9, 3}
	bc.Labels = []string{"S0", "S1", "S2", "S3", "S4", "S5"}
	bc.Title = "Bar Chart"
	bc.SetRect(5, 5, 100, 25)
	bc.BarWidth = 5
	bc.BarColors = []ui.Color{ui.ColorRed, ui.ColorGreen}
	bc.LabelStyles = []ui.Style{ui.NewStyle(ui.ColorBlue)}
	bc.NumStyles = []ui.Style{ui.NewStyle(ui.ColorYellow)}

	ui.Render(bc)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/canvas.go
================================================
// +build ignore

package main

import (
	"image"
	"log"

	ui "github.com/gizak/termui/v3"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	c := ui.NewCanvas()
	c.SetRect(0, 0, 50, 50)
	c.SetLine(image.Pt(0, 0), image.Pt(10, 20), ui.ColorWhite)

	ui.Render(c)

	for e := range ui.PollEvents() {
		if e.Type == ui.KeyboardEvent {
			break
		}
	}
}


================================================
FILE: _examples/demo.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"
	"math"
	"time"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	p := widgets.NewParagraph()
	p.Title = "Text Box"
	p.Text = "PRESS q TO QUIT DEMO"
	p.SetRect(0, 0, 50, 5)
	p.TextStyle.Fg = ui.ColorWhite
	p.BorderStyle.Fg = ui.ColorCyan

	updateParagraph := func(count int) {
		if count%2 == 0 {
			p.TextStyle.Fg = ui.ColorRed
		} else {
			p.TextStyle.Fg = ui.ColorWhite
		}
	}

	listData := []string{
		"[0] gizak/termui",
		"[1] editbox.go",
		"[2] interrupt.go",
		"[3] keyboard.go",
		"[4] output.go",
		"[5] random_out.go",
		"[6] dashboard.go",
		"[7] nsf/termbox-go",
	}

	l := widgets.NewList()
	l.Title = "List"
	l.Rows = listData
	l.SetRect(0, 5, 25, 12)
	l.TextStyle.Fg = ui.ColorYellow

	g := widgets.NewGauge()
	g.Title = "Gauge"
	g.Percent = 50
	g.SetRect(0, 12, 50, 15)
	g.BarColor = ui.ColorRed
	g.BorderStyle.Fg = ui.ColorWhite
	g.TitleStyle.Fg = ui.ColorCyan

	sparklineData := []float64{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}

	sl := widgets.NewSparkline()
	sl.Title = "srv 0:"
	sl.Data = sparklineData
	sl.LineColor = ui.ColorCyan
	sl.TitleStyle.Fg = ui.ColorWhite

	sl2 := widgets.NewSparkline()
	sl2.Title = "srv 1:"
	sl2.Data = sparklineData
	sl2.TitleStyle.Fg = ui.ColorWhite
	sl2.LineColor = ui.ColorRed

	slg := widgets.NewSparklineGroup(sl, sl2)
	slg.Title = "Sparkline"
	slg.SetRect(25, 5, 50, 12)

	sinData := (func() []float64 {
		n := 220
		ps := make([]float64, n)
		for i := range ps {
			ps[i] = 1 + math.Sin(float64(i)/5)
		}
		return ps
	})()

	lc := widgets.NewPlot()
	lc.Title = "dot-marker Line Chart"
	lc.Data = make([][]float64, 1)
	lc.Data[0] = sinData
	lc.SetRect(0, 15, 50, 25)
	lc.AxesColor = ui.ColorWhite
	lc.LineColors[0] = ui.ColorRed
	lc.Marker = widgets.MarkerDot

	barchartData := []float64{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}

	bc := widgets.NewBarChart()
	bc.Title = "Bar Chart"
	bc.SetRect(50, 0, 75, 10)
	bc.Labels = []string{"S0", "S1", "S2", "S3", "S4", "S5"}
	bc.BarColors[0] = ui.ColorGreen
	bc.NumStyles[0] = ui.NewStyle(ui.ColorBlack)

	lc2 := widgets.NewPlot()
	lc2.Title = "braille-mode Line Chart"
	lc2.Data = make([][]float64, 1)
	lc2.Data[0] = sinData
	lc2.SetRect(50, 15, 75, 25)
	lc2.AxesColor = ui.ColorWhite
	lc2.LineColors[0] = ui.ColorYellow

	p2 := widgets.NewParagraph()
	p2.Text = "Hey!\nI am a borderless block!"
	p2.Border = false
	p2.SetRect(50, 10, 75, 10)
	p2.TextStyle.Fg = ui.ColorMagenta

	draw := func(count int) {
		g.Percent = count % 101
		l.Rows = listData[count%9:]
		slg.Sparklines[0].Data = sparklineData[:30+count%50]
		slg.Sparklines[1].Data = sparklineData[:35+count%50]
		lc.Data[0] = sinData[count/2%220:]
		lc2.Data[0] = sinData[2*count%220:]
		bc.Data = barchartData[count/2%10:]

		ui.Render(p, l, g, slg, lc, bc, lc2, p2)
	}

	tickerCount := 1
	draw(tickerCount)
	tickerCount++
	uiEvents := ui.PollEvents()
	ticker := time.NewTicker(time.Second).C
	for {
		select {
		case e := <-uiEvents:
			switch e.ID {
			case "q", "<C-c>":
				return
			}
		case <-ticker:
			updateParagraph(tickerCount)
			draw(tickerCount)
			tickerCount++
		}
	}
}


================================================
FILE: _examples/gauge.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"fmt"
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	g0 := widgets.NewGauge()
	g0.Title = "Slim Gauge"
	g0.SetRect(20, 20, 30, 30)
	g0.Percent = 75
	g0.BarColor = ui.ColorRed
	g0.BorderStyle.Fg = ui.ColorWhite
	g0.TitleStyle.Fg = ui.ColorCyan

	g2 := widgets.NewGauge()
	g2.Title = "Slim Gauge"
	g2.SetRect(0, 3, 50, 6)
	g2.Percent = 60
	g2.BarColor = ui.ColorYellow
	g2.LabelStyle = ui.NewStyle(ui.ColorBlue)
	g2.BorderStyle.Fg = ui.ColorWhite

	g1 := widgets.NewGauge()
	g1.Title = "Big Gauge"
	g1.SetRect(0, 6, 50, 11)
	g1.Percent = 30
	g1.BarColor = ui.ColorGreen
	g1.LabelStyle = ui.NewStyle(ui.ColorYellow)
	g1.TitleStyle.Fg = ui.ColorMagenta
	g1.BorderStyle.Fg = ui.ColorWhite

	g3 := widgets.NewGauge()
	g3.Title = "Gauge with custom label"
	g3.SetRect(0, 11, 50, 14)
	g3.Percent = 50
	g3.Label = fmt.Sprintf("%v%% (100MBs free)", g3.Percent)

	g4 := widgets.NewGauge()
	g4.Title = "Gauge"
	g4.SetRect(0, 14, 50, 17)
	g4.Percent = 50
	g4.Label = "Gauge with custom highlighted label"
	g4.BarColor = ui.ColorGreen
	g4.LabelStyle = ui.NewStyle(ui.ColorYellow)

	ui.Render(g0, g1, g2, g3, g4)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/grid.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"
	"math"
	"time"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	sinFloat64 := (func() []float64 {
		n := 400
		data := make([]float64, n)
		for i := range data {
			data[i] = 1 + math.Sin(float64(i)/5)
		}
		return data
	})()

	sl := widgets.NewSparkline()
	sl.Data = sinFloat64[:100]
	sl.LineColor = ui.ColorCyan
	sl.TitleStyle.Fg = ui.ColorWhite

	slg := widgets.NewSparklineGroup(sl)
	slg.Title = "Sparkline"

	lc := widgets.NewPlot()
	lc.Title = "braille-mode Line Chart"
	lc.Data = append(lc.Data, sinFloat64)
	lc.AxesColor = ui.ColorWhite
	lc.LineColors[0] = ui.ColorYellow

	gs := make([]*widgets.Gauge, 3)
	for i := range gs {
		gs[i] = widgets.NewGauge()
		gs[i].Percent = i * 10
		gs[i].BarColor = ui.ColorRed
	}

	ls := widgets.NewList()
	ls.Rows = []string{
		"[1] Downloading File 1",
		"",
		"",
		"",
		"[2] Downloading File 2",
		"",
		"",
		"",
		"[3] Uploading File 3",
	}
	ls.Border = false

	p := widgets.NewParagraph()
	p.Text = "<> This row has 3 columns\n<- Widgets can be stacked up like left side\n<- Stacked widgets are treated as a single widget"
	p.Title = "Demonstration"

	grid := ui.NewGrid()
	termWidth, termHeight := ui.TerminalDimensions()
	grid.SetRect(0, 0, termWidth, termHeight)

	grid.Set(
		ui.NewRow(1.0/2,
			ui.NewCol(1.0/2, slg),
			ui.NewCol(1.0/2, lc),
		),
		ui.NewRow(1.0/2,
			ui.NewCol(1.0/4, ls),
			ui.NewCol(1.0/4,
				ui.NewRow(.9/3, gs[0]),
				ui.NewRow(.9/3, gs[1]),
				ui.NewRow(1.2/3, gs[2]),
			),
			ui.NewCol(1.0/2, p),
		),
	)

	ui.Render(grid)

	tickerCount := 1
	uiEvents := ui.PollEvents()
	ticker := time.NewTicker(time.Second).C
	for {
		select {
		case e := <-uiEvents:
			switch e.ID {
			case "q", "<C-c>":
				return
			case "<Resize>":
				payload := e.Payload.(ui.Resize)
				grid.SetRect(0, 0, payload.Width, payload.Height)
				ui.Clear()
				ui.Render(grid)
			}
		case <-ticker:
			if tickerCount == 100 {
				return
			}
			for _, g := range gs {
				g.Percent = (g.Percent + 3) % 100
			}
			slg.Sparklines[0].Data = sinFloat64[tickerCount : tickerCount+100]
			lc.Data[0] = sinFloat64[2*tickerCount:]
			ui.Render(grid)
			tickerCount++
		}
	}
}


================================================
FILE: _examples/hello_world.go
================================================
// +build ignore

package main

import (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/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
		}
	}
}


================================================
FILE: _examples/image.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"encoding/base64"
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"log"
	"net/http"
	"os"
	"strings"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	var images []image.Image
	for _, arg := range os.Args[1:] {
		resp, err := http.Get(arg)
		if err != nil {
			log.Fatalf("failed to fetch image: %v", err)
		}
		image, _, err := image.Decode(resp.Body)
		if err != nil {
			log.Fatalf("failed to decode fetched image: %v", err)
		}
		images = append(images, image)
	}
	if len(images) == 0 {
		image, _, err := image.Decode(base64.NewDecoder(base64.StdEncoding, strings.NewReader(GOPHER_IMAGE)))
		if err != nil {
			log.Fatalf("failed to decode gopher image: %v", err)
		}
		images = append(images, image)
	}

	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	img := widgets.NewImage(nil)
	img.SetRect(0, 0, 100, 50)
	index := 0
	render := func() {
		img.Image = images[index]
		if !img.Monochrome {
			img.Title = fmt.Sprintf("Color %d/%d", index+1, len(images))
		} else if !img.MonochromeInvert {
			img.Title = fmt.Sprintf("Monochrome(%d) %d/%d", img.MonochromeThreshold, index+1, len(images))
		} else {
			img.Title = fmt.Sprintf("InverseMonochrome(%d) %d/%d", img.MonochromeThreshold, index+1, len(images))
		}
		ui.Render(img)
	}
	render()

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		case "<Left>", "h":
			index = (index + len(images) - 1) % len(images)
		case "<Right>", "l":
			index = (index + 1) % len(images)
		case "<Up>", "k":
			img.MonochromeThreshold++
		case "<Down>", "j":
			img.MonochromeThreshold--
		case "<Enter>":
			img.Monochrome = !img.Monochrome
		case "<Tab>":
			img.MonochromeInvert = !img.MonochromeInvert
		}
		render()
	}
}

const GOPHER_IMAGE = `iVBORw0KGgoAAAANSUhEUgAAAEsAAAA8CAAAAAALAhhPAAAFfUlEQVRYw62XeWwUVRzHf2+OPbo9d7tsWyiyaZti6eWGAhISoIGKECEKCAiJJkYTiUgTMYSIosYYBBIUIxoSPIINEBDi2VhwkQrVsj1ESgu9doHWdrul7ba73WNm3vOPtsseM9MdwvvrzTs+8/t95ze/33sI5BqiabU6m9En8oNjduLnAEDLUsQXFF8tQ5oxK3vmnNmDSMtrncks9Hhtt/qeWZapHb1ha3UqYSWVl2ZmpWgaXMXGohQAvmeop3bjTRtv6SgaK/Pb9/bFzUrYslbFAmHPp+3WhAYdr+7GN/YnpN46Opv55VDsJkoEpMrY/vO2BIYQ6LLvm0ThY3MzDzzeSJeeWNyTkgnIE5ePKsvKlcg/0T9QMzXalwXMlj54z4c0rh/mzEfr+FgWEz2w6uk8dkzFAgcARAgNp1ZYef8bH2AgvuStbc2/i6CiWGj98y2tw2l4FAXKkQBIf+exyRnteY83LfEwDQAYCoK+P6bxkZm/0966LxcAAILHB56kgD95PPxltuYcMtFTWw/FKkY/6Opf3GGd9ZF+Qp6mzJxzuRSractOmJrH1u8XTvWFHINNkLQLMR+XHXvfPPHw967raE1xxwtA36IMRfkAAG29/7mLuQcb2WOnsJReZGfpiHsSBX81cvMKywYZHhX5hFPtOqPGWZCXnhWGAu6lX91ElKXSalcLXu3UaOXVay57ZSe5f6Gpx7J2MXAsi7EqSp09b/MirKSyJfnfEEgeDjl8FgDAfvewP03zZ+AJ0m9aFRM8eEHBDRKjfcreDXnZdQuAxXpT2NRJ7xl3UkLBhuVGU16gZiGOgZmrSbRdqkILuL/yYoSXHHkl9KXgqNu3PB8oRg0geC5vFmLjad6mUyTKLmF3OtraWDIfACyXqmephaDABawfpi6tqqBZytfQMqOz6S09iWXhktrRaB8Xz4Yi/8gyABDm5NVe6qq/3VzPrcjELWrebVuyY2T7ar4zQyybUCtsQ5Es1FGaZVrRVQwAgHGW2ZCRZshI5bGQi7HesyE972pOSeMM0dSktlzxRdrlqb3Osa6CCS8IJoQQQgBAbTAa5l5epO34rJszibJI8rxLfGzcp1dRosutGeb2VDNgqYrwTiPNsLxXiPi3dz7LiS1WBRBDBOnqEjyy3aQb+/bLiJzz9dIkscVBBLxMfSEac7kO4Fpkngi0ruNBeSOal+u8jgOuqPz12nryMLCniEjtOOOmpt+KEIqsEdocJjYXwrh9OZqWJQyPCTo67LNS/TdxLAv6R5ZNK9npEjbYdT33gRo4o5oTqR34R+OmaSzDBWsAIPhuRcgyoteNi9gF0KzNYWVItPf2TLoXEg+7isNC7uJkgo1iQWOfRSP9NR11RtbZZ3OMG/VhL6jvx+J1m87+RCfJChAtEBQkSBX2PnSiihc/Twh3j0h7qdYQAoRVsRGmq7HU2QRbaxVGa1D6nIOqaIWRjyRZpHMQKWKpZM5feA+lzC4ZFultV8S6T0mzQGhQohi5I8iw+CsqBSxhFMuwyLgSwbghGb0AiIKkSDmGZVmJSiKihsiyOAUs70UkywooYP0bii9GdH4sfr1UNysd3fUyLLMQN+rsmo3grHl9VNJHbbwxoa47Vw5gupIqrZcjPh9R4Nye3nRDk199V+aetmvVtDRE8/+cbgAAgMIWGb3UA0MGLE9SCbWX670TDy1y98c3D27eppUjsZ6fql3jcd5rUe7+ZIlLNQny3Rd+E5Tct3WVhTM5RBCEdiEK0b6B+/ca2gYU393nFj/n1AygRQxPIUA043M42u85+z2SnssKrPl8Mx76NL3E6eXc3be7OD+H4WHbJkKI8AU8irbITQjZ+0hQcPEgId/Fn/pl9crKH02+5o2b9T/eMx7pKoskYgAAAABJRU5ErkJggg==`


================================================
FILE: _examples/list.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	l := widgets.NewList()
	l.Title = "List"
	l.Rows = []string{
		"[0] github.com/gizak/termui/v3",
		"[1] [你好,世界](fg:blue)",
		"[2] [こんにちは世界](fg:red)",
		"[3] [color](fg:white,bg:green) output",
		"[4] output.go",
		"[5] random_out.go",
		"[6] dashboard.go",
		"[7] foo",
		"[8] bar",
		"[9] baz",
	}
	l.TextStyle = ui.NewStyle(ui.ColorYellow)
	l.WrapText = false
	l.SetRect(0, 0, 25, 8)

	ui.Render(l)

	previousKey := ""
	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		case "j", "<Down>":
			l.ScrollDown()
		case "k", "<Up>":
			l.ScrollUp()
		case "<C-d>":
			l.ScrollHalfPageDown()
		case "<C-u>":
			l.ScrollHalfPageUp()
		case "<C-f>":
			l.ScrollPageDown()
		case "<C-b>":
			l.ScrollPageUp()
		case "g":
			if previousKey == "g" {
				l.ScrollTop()
			}
		case "<Home>":
			l.ScrollTop()
		case "G", "<End>":
			l.ScrollBottom()
		}

		if previousKey == "g" {
			previousKey = ""
		} else {
			previousKey = e.ID
		}

		ui.Render(l)
	}
}


================================================
FILE: _examples/paragraph.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	p0 := widgets.NewParagraph()
	p0.Text = "Borderless Text"
	p0.SetRect(0, 0, 20, 5)
	p0.Border = false

	p1 := widgets.NewParagraph()
	p1.Title = "标签"
	p1.Text = "你好,世界。"
	p1.SetRect(20, 0, 35, 5)

	p2 := widgets.NewParagraph()
	p2.Title = "Multiline"
	p2.Text = "Simple colored text\nwith label. It [can be](fg:red) multilined with \\n or [break automatically](fg:red,fg:bold)"
	p2.SetRect(0, 5, 35, 10)
	p2.BorderStyle.Fg = ui.ColorYellow

	p3 := widgets.NewParagraph()
	p3.Title = "Auto Trim"
	p3.Text = "Long text with label and it is auto trimmed."
	p3.SetRect(0, 10, 40, 15)

	p4 := widgets.NewParagraph()
	p4.Title = "Text Box with Wrapping"
	p4.Text = "Press q to QUIT THE DEMO. [There](fg:blue,mod:bold) are other things [that](fg:red) are going to fit in here I think. What do you think? Now is the time for all good [men to](bg:blue) come to the aid of their country. [This is going to be one really really really long line](fg:green) that is going to go together and stuffs and things. Let's see how this thing renders out.\n    Here is a new paragraph and stuffs and things. There should be a tab indent at the beginning of the paragraph. Let's see if that worked as well."
	p4.SetRect(40, 0, 70, 20)
	p4.BorderStyle.Fg = ui.ColorBlue

	ui.Render(p0, p1, p2, p3, p4)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/piechart.go
================================================
// +build ignore

package main

import (
	"fmt"
	"log"
	"math"
	"math/rand"
	"time"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

var run = true

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	rand.Seed(time.Now().UTC().UnixNano())
	randomDataAndOffset := func() (data []float64, offset float64) {
		noSlices := 1 + rand.Intn(5)
		data = make([]float64, noSlices)
		for i := range data {
			data[i] = rand.Float64()
		}
		offset = 2.0 * math.Pi * rand.Float64()
		return
	}

	pc := widgets.NewPieChart()
	pc.Title = "Pie Chart"
	pc.SetRect(5, 5, 70, 36)
	pc.Data = []float64{.25, .25, .25, .25}
	pc.AngleOffset = -.5 * math.Pi
	pc.LabelFormatter = func(i int, v float64) string {
		return fmt.Sprintf("%.02f", v)
	}

	pause := func() {
		run = !run
		if run {
			pc.Title = "Pie Chart"
		} else {
			pc.Title = "Pie Chart (Stopped)"
		}
		ui.Render(pc)
	}

	ui.Render(pc)

	uiEvents := ui.PollEvents()
	ticker := time.NewTicker(time.Second).C
	for {
		select {
		case e := <-uiEvents:
			switch e.ID {
			case "q", "<C-c>":
				return
			case "s":
				pause()
			}
		case <-ticker:
			if run {
				pc.Data, pc.AngleOffset = randomDataAndOffset()
				ui.Render(pc)
			}
		}
	}
}


================================================
FILE: _examples/plot.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"
	"math"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	sinData := func() [][]float64 {
		n := 220
		data := make([][]float64, 2)
		data[0] = make([]float64, n)
		data[1] = make([]float64, n)
		for i := 0; i < n; i++ {
			data[0][i] = 1 + math.Sin(float64(i)/5)
			data[1][i] = 1 + math.Cos(float64(i)/5)
		}
		return data
	}()

	p0 := widgets.NewPlot()
	p0.Title = "braille-mode Line Chart"
	p0.Data = sinData
	p0.SetRect(0, 0, 50, 15)
	p0.AxesColor = ui.ColorWhite
	p0.LineColors[0] = ui.ColorGreen

	p1 := widgets.NewPlot()
	p1.Title = "dot-mode line Chart"
	p1.Marker = widgets.MarkerDot
	p1.Data = [][]float64{[]float64{1, 2, 3, 4, 5}}
	p1.SetRect(50, 0, 75, 10)
	p1.DotMarkerRune = '+'
	p1.AxesColor = ui.ColorWhite
	p1.LineColors[0] = ui.ColorYellow
	p1.DrawDirection = widgets.DrawLeft

	p2 := widgets.NewPlot()
	p2.Title = "dot-mode Scatter Plot"
	p2.Marker = widgets.MarkerDot
	p2.Data = make([][]float64, 2)
	p2.Data[0] = []float64{1, 2, 3, 4, 5}
	p2.Data[1] = sinData[1][4:]
	p2.SetRect(0, 15, 50, 30)
	p2.AxesColor = ui.ColorWhite
	p2.LineColors[0] = ui.ColorCyan
	p2.PlotType = widgets.ScatterPlot

	p3 := widgets.NewPlot()
	p3.Title = "braille-mode Scatter Plot"
	p3.Data = make([][]float64, 2)
	p3.Data[0] = []float64{1, 2, 3, 4, 5}
	p3.Data[1] = sinData[1][4:]
	p3.SetRect(45, 15, 80, 30)
	p3.AxesColor = ui.ColorWhite
	p3.LineColors[0] = ui.ColorCyan
	p3.Marker = widgets.MarkerBraille
	p3.PlotType = widgets.ScatterPlot

	ui.Render(p0, p1, p2, p3)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/sparkline.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	data := []float64{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}

	sl0 := widgets.NewSparkline()
	sl0.Data = data[3:]
	sl0.LineColor = ui.ColorGreen

	// single
	slg0 := widgets.NewSparklineGroup(sl0)
	slg0.Title = "Sparkline 0"
	slg0.SetRect(0, 0, 20, 10)

	sl1 := widgets.NewSparkline()
	sl1.Title = "Sparkline 1"
	sl1.Data = data
	sl1.LineColor = ui.ColorRed

	sl2 := widgets.NewSparkline()
	sl2.Title = "Sparkline 2"
	sl2.Data = data[5:]
	sl2.LineColor = ui.ColorMagenta

	slg1 := widgets.NewSparklineGroup(sl0, sl1, sl2)
	slg1.Title = "Group Sparklines"
	slg1.SetRect(0, 10, 25, 25)

	sl3 := widgets.NewSparkline()
	sl3.Title = "Enlarged Sparkline"
	sl3.Data = data
	sl3.LineColor = ui.ColorYellow

	slg2 := widgets.NewSparklineGroup(sl3)
	slg2.Title = "Tweeked Sparkline"
	slg2.SetRect(20, 0, 50, 10)
	slg2.BorderStyle.Fg = ui.ColorCyan

	ui.Render(slg0, slg1, slg2)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/stacked_barchart.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	sbc := widgets.NewStackedBarChart()
	sbc.Title = "Student's Marks: X-Axis=Name, Y-Axis=Grade% (Math, English, Science, Computer Science)"
	sbc.Labels = []string{"Ken", "Rob", "Dennis", "Linus"}

	sbc.Data = make([][]float64, 4)
	sbc.Data[0] = []float64{90, 85, 90, 80}
	sbc.Data[1] = []float64{70, 85, 75, 60}
	sbc.Data[2] = []float64{75, 60, 80, 85}
	sbc.Data[3] = []float64{100, 100, 100, 100}
	sbc.SetRect(5, 5, 100, 30)
	sbc.BarWidth = 5

	ui.Render(sbc)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/table.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	table1 := widgets.NewTable()
	table1.Rows = [][]string{
		[]string{"header1", "header2", "header3"},
		[]string{"你好吗", "Go-lang is so cool", "Im working on Ruby"},
		[]string{"2016", "10", "11"},
	}
	table1.TextStyle = ui.NewStyle(ui.ColorWhite)
	table1.SetRect(0, 0, 60, 10)

	ui.Render(table1)

	table2 := widgets.NewTable()
	table2.Rows = [][]string{
		[]string{"header1", "header2", "header3"},
		[]string{"Foundations", "Go-lang is so cool", "Im working on Ruby"},
		[]string{"2016", "11", "11"},
	}
	table2.TextStyle = ui.NewStyle(ui.ColorWhite)
	table2.TextAlignment = ui.AlignCenter
	table2.RowSeparator = false
	table2.SetRect(0, 10, 20, 20)

	ui.Render(table2)

	table3 := widgets.NewTable()
	table3.Rows = [][]string{
		[]string{"header1", "header2", "header3"},
		[]string{"AAA", "BBB", "CCC"},
		[]string{"DDD", "EEE", "FFF"},
		[]string{"GGG", "HHH", "III"},
	}
	table3.TextStyle = ui.NewStyle(ui.ColorWhite)
	table3.RowSeparator = true
	table3.BorderStyle = ui.NewStyle(ui.ColorGreen)
	table3.SetRect(0, 30, 70, 20)
	table3.FillRow = true
	table3.RowStyles[0] = ui.NewStyle(ui.ColorWhite, ui.ColorBlack, ui.ModifierBold)
	table3.RowStyles[2] = ui.NewStyle(ui.ColorWhite, ui.ColorRed, ui.ModifierBold)
	table3.RowStyles[3] = ui.NewStyle(ui.ColorYellow)

	ui.Render(table3)

	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		}
	}
}


================================================
FILE: _examples/tabs.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	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

	p2 := widgets.NewParagraph()
	p2.Text = "Press q to quit\nPress h or l to switch tabs\n"
	p2.Title = "Keys"
	p2.SetRect(5, 5, 40, 15)
	p2.BorderStyle.Fg = ui.ColorYellow

	bc := widgets.NewBarChart()
	bc.Title = "Bar Chart"
	bc.Data = []float64{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}
	bc.SetRect(5, 5, 35, 10)
	bc.Labels = []string{"S0", "S1", "S2", "S3", "S4", "S5"}

	tabpane := widgets.NewTabPane("pierwszy", "drugi", "trzeci", "żółw", "four", "five")
	tabpane.SetRect(0, 1, 50, 4)
	tabpane.Border = true

	renderTab := func() {
		switch tabpane.ActiveTabIndex {
		case 0:
			ui.Render(p2)
		case 1:
			ui.Render(bc)
		}
	}

	ui.Render(header, tabpane, p2)

	uiEvents := ui.PollEvents()

	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		case "h":
			tabpane.FocusLeft()
			ui.Clear()
			ui.Render(header, tabpane)
			renderTab()
		case "l":
			tabpane.FocusRight()
			ui.Clear()
			ui.Render(header, tabpane)
			renderTab()
		}
	}
}


================================================
FILE: _examples/tree.go
================================================
/// +build ignore

package main

import (
	"log"

	ui "github.com/gizak/termui/v3"
	"github.com/gizak/termui/v3/widgets"
)

type nodeValue string

func (nv nodeValue) String() string {
	return string(nv)
}

func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	nodes := []*widgets.TreeNode{
		{
			Value: nodeValue("Key 1"),
			Nodes: []*widgets.TreeNode{
				{
					Value: nodeValue("Key 1.1"),
					Nodes: []*widgets.TreeNode{
						{
							Value: nodeValue("Key 1.1.1"),
							Nodes: nil,
						},
						{
							Value: nodeValue("Key 1.1.2"),
							Nodes: nil,
						},
					},
				},
				{
					Value: nodeValue("Key 1.2"),
					Nodes: nil,
				},
			},
		},
		{
			Value: nodeValue("Key 2"),
			Nodes: []*widgets.TreeNode{
				{
					Value: nodeValue("Key 2.1"),
					Nodes: nil,
				},
				{
					Value: nodeValue("Key 2.2"),
					Nodes: nil,
				},
				{
					Value: nodeValue("Key 2.3"),
					Nodes: nil,
				},
			},
		},
		{
			Value: nodeValue("Key 3"),
			Nodes: nil,
		},
	}

	l := widgets.NewTree()
	l.TextStyle = ui.NewStyle(ui.ColorYellow)
	l.WrapText = false
	l.SetNodes(nodes)

	x, y := ui.TerminalDimensions()

	l.SetRect(0, 0, x, y)

	ui.Render(l)

	previousKey := ""
	uiEvents := ui.PollEvents()
	for {
		e := <-uiEvents
		switch e.ID {
		case "q", "<C-c>":
			return
		case "j", "<Down>":
			l.ScrollDown()
		case "k", "<Up>":
			l.ScrollUp()
		case "<C-d>":
			l.ScrollHalfPageDown()
		case "<C-u>":
			l.ScrollHalfPageUp()
		case "<C-f>":
			l.ScrollPageDown()
		case "<C-b>":
			l.ScrollPageUp()
		case "g":
			if previousKey == "g" {
				l.ScrollTop()
			}
		case "<Home>":
			l.ScrollTop()
		case "<Enter>":
			l.ToggleExpand()
		case "G", "<End>":
			l.ScrollBottom()
		case "E":
			l.ExpandAll()
		case "C":
			l.CollapseAll()
		case "<Resize>":
			x, y := ui.TerminalDimensions()
			l.SetRect(0, 0, x, y)
		}

		if previousKey == "g" {
			previousKey = ""
		} else {
			previousKey = e.ID
		}

		ui.Render(l)
	}
}


================================================
FILE: _scripts/copyright_header.py
================================================
#!/usr/bin/env python3

import re
import os
import io

copyright = """// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

"""

exclude_dirs = [".git", "_docs"]
exclude_files = []
include_dirs = [".", "debug", "extra", "test", "_example"]


def is_target(fpath):
    if os.path.splitext(fpath)[-1] == ".go":
        return True
    return False


def update_copyright(fpath):
    print("processing " + fpath)
    f = io.open(fpath, 'r', encoding='utf-8')
    fstr = f.read()
    f.close()

    # remove old
    m = re.search('^// Copyright .+?\r?\n\r?\n', fstr, re.MULTILINE|re.DOTALL)
    if m:
        fstr = fstr[m.end():]

    # add new
    fstr = copyright + fstr
    f = io.open(fpath, 'w',encoding='utf-8')
    f.write(fstr)
    f.close()


def main():
    for d in include_dirs:
        files = [
            os.path.join(d, f) for f in os.listdir(d)
            if os.path.isfile(os.path.join(d, f))
        ]
        for f in files:
            if is_target(f):
                update_copyright(f)


if __name__ == '__main__':
    main()


================================================
FILE: _test/log_events.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	"fmt"
	"log"

	ui "github.com/gizak/termui/v3"
)

// logs all events to the termui window
// stdout can also be redirected to a file and read with `tail -f`
func main() {
	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

	events := ui.PollEvents()
	for {
		e := <-events
		fmt.Printf("%v", e)
		switch e.ID {
		case "q", "<C-c>":
			return
		case "<MouseLeft>":
			return
		}
	}
}


================================================
FILE: alignment.go
================================================
package termui

type Alignment uint

const (
	AlignLeft Alignment = iota
	AlignCenter
	AlignRight
)


================================================
FILE: backend.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 (
	tb "github.com/nsf/termbox-go"
)

// Init initializes termbox-go and is required to render anything.
// After initialization, the library must be finalized with `Close`.
func Init() error {
	if err := tb.Init(); err != nil {
		return err
	}
	tb.SetInputMode(tb.InputEsc | tb.InputMouse)
	tb.SetOutputMode(tb.Output256)
	return nil
}

// Close closes termbox-go.
func Close() {
	tb.Close()
}

func TerminalDimensions() (int, int) {
	tb.Sync()
	width, height := tb.Size()
	return width, height
}

func Clear() {
	tb.Clear(tb.ColorDefault, tb.Attribute(Theme.Default.Bg+1))
}


================================================
FILE: block.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import (
	"image"
	"sync"
)

// Block is the base struct inherited by most widgets.
// Block manages size, position, border, and title.
// It implements all 3 of the methods needed for the `Drawable` interface.
// Custom widgets will override the Draw method.
type Block struct {
	Border      bool
	BorderStyle Style

	BorderLeft, BorderRight, BorderTop, BorderBottom bool

	PaddingLeft, PaddingRight, PaddingTop, PaddingBottom int

	image.Rectangle
	Inner image.Rectangle

	Title      string
	TitleStyle Style

	sync.Mutex
}

func NewBlock() *Block {
	return &Block{
		Border:       true,
		BorderStyle:  Theme.Block.Border,
		BorderLeft:   true,
		BorderRight:  true,
		BorderTop:    true,
		BorderBottom: true,

		TitleStyle: Theme.Block.Title,
	}
}

func (self *Block) drawBorder(buf *Buffer) {
	verticalCell := Cell{VERTICAL_LINE, self.BorderStyle}
	horizontalCell := Cell{HORIZONTAL_LINE, self.BorderStyle}

	// draw lines
	if self.BorderTop {
		buf.Fill(horizontalCell, image.Rect(self.Min.X, self.Min.Y, self.Max.X, self.Min.Y+1))
	}
	if self.BorderBottom {
		buf.Fill(horizontalCell, image.Rect(self.Min.X, self.Max.Y-1, self.Max.X, self.Max.Y))
	}
	if self.BorderLeft {
		buf.Fill(verticalCell, image.Rect(self.Min.X, self.Min.Y, self.Min.X+1, self.Max.Y))
	}
	if self.BorderRight {
		buf.Fill(verticalCell, image.Rect(self.Max.X-1, self.Min.Y, self.Max.X, self.Max.Y))
	}

	// draw corners
	if self.BorderTop && self.BorderLeft {
		buf.SetCell(Cell{TOP_LEFT, self.BorderStyle}, self.Min)
	}
	if self.BorderTop && self.BorderRight {
		buf.SetCell(Cell{TOP_RIGHT, self.BorderStyle}, image.Pt(self.Max.X-1, self.Min.Y))
	}
	if self.BorderBottom && self.BorderLeft {
		buf.SetCell(Cell{BOTTOM_LEFT, self.BorderStyle}, image.Pt(self.Min.X, self.Max.Y-1))
	}
	if self.BorderBottom && self.BorderRight {
		buf.SetCell(Cell{BOTTOM_RIGHT, self.BorderStyle}, self.Max.Sub(image.Pt(1, 1)))
	}
}

// Draw implements the Drawable interface.
func (self *Block) Draw(buf *Buffer) {
	if self.Border {
		self.drawBorder(buf)
	}
	buf.SetString(
		self.Title,
		self.TitleStyle,
		image.Pt(self.Min.X+2, self.Min.Y),
	)
}

// SetRect implements the Drawable interface.
func (self *Block) SetRect(x1, y1, x2, y2 int) {
	self.Rectangle = image.Rect(x1, y1, x2, y2)
	self.Inner = image.Rect(
		self.Min.X+1+self.PaddingLeft,
		self.Min.Y+1+self.PaddingTop,
		self.Max.X-1-self.PaddingRight,
		self.Max.Y-1-self.PaddingBottom,
	)
}

// GetRect implements the Drawable interface.
func (self *Block) GetRect() image.Rectangle {
	return self.Rectangle
}


================================================
FILE: buffer.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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"

	rw "github.com/mattn/go-runewidth"
)

// Cell represents a viewable terminal cell
type Cell struct {
	Rune  rune
	Style Style
}

var CellClear = Cell{
	Rune:  ' ',
	Style: StyleClear,
}

// NewCell takes 1 to 2 arguments
// 1st argument = rune
// 2nd argument = optional style
func NewCell(rune rune, args ...interface{}) Cell {
	style := StyleClear
	if len(args) == 1 {
		style = args[0].(Style)
	}
	return Cell{
		Rune:  rune,
		Style: style,
	}
}

// Buffer represents a section of a terminal and is a renderable rectangle of cells.
type Buffer struct {
	image.Rectangle
	CellMap map[image.Point]Cell
}

func NewBuffer(r image.Rectangle) *Buffer {
	buf := &Buffer{
		Rectangle: r,
		CellMap:   make(map[image.Point]Cell),
	}
	buf.Fill(CellClear, r) // clears out area
	return buf
}

func (self *Buffer) GetCell(p image.Point) Cell {
	return self.CellMap[p]
}

func (self *Buffer) SetCell(c Cell, p image.Point) {
	self.CellMap[p] = c
}

func (self *Buffer) Fill(c Cell, rect image.Rectangle) {
	for x := rect.Min.X; x < rect.Max.X; x++ {
		for y := rect.Min.Y; y < rect.Max.Y; y++ {
			self.SetCell(c, image.Pt(x, y))
		}
	}
}

func (self *Buffer) SetString(s string, style Style, p image.Point) {
	runes := []rune(s)
	x := 0
	for _, char := range runes {
		self.SetCell(Cell{char, style}, image.Pt(p.X+x, p.Y))
		x += rw.RuneWidth(char)
	}
}


================================================
FILE: canvas.go
================================================
package termui

import (
	"image"

	"github.com/gizak/termui/v3/drawille"
)

type Canvas struct {
	Block
	drawille.Canvas
}

func NewCanvas() *Canvas {
	return &Canvas{
		Block:  *NewBlock(),
		Canvas: *drawille.NewCanvas(),
	}
}

func (self *Canvas) SetPoint(p image.Point, color Color) {
	self.Canvas.SetPoint(p, drawille.Color(color))
}

func (self *Canvas) SetLine(p0, p1 image.Point, color Color) {
	self.Canvas.SetLine(p0, p1, drawille.Color(color))
}

func (self *Canvas) Draw(buf *Buffer) {
	for point, cell := range self.Canvas.GetCells() {
		if point.In(self.Rectangle) {
			convertedCell := Cell{
				cell.Rune,
				Style{
					Color(cell.Color),
					ColorClear,
					ModifierClear,
				},
			}
			buf.SetCell(convertedCell, point)
		}
	}
}


================================================
FILE: doc.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 for creating terminal user interfaces (TUIs) using widgets.
*/
package termui


================================================
FILE: drawille/drawille.go
================================================
package drawille

import (
	"image"
)

const BRAILLE_OFFSET = '\u2800'

var BRAILLE = [4][2]rune{
	{'\u0001', '\u0008'},
	{'\u0002', '\u0010'},
	{'\u0004', '\u0020'},
	{'\u0040', '\u0080'},
}

type Color int

type Cell struct {
	Rune  rune
	Color Color
}

type Canvas struct {
	CellMap map[image.Point]Cell
}

func NewCanvas() *Canvas {
	return &Canvas{
		CellMap: make(map[image.Point]Cell),
	}
}

func (self *Canvas) SetPoint(p image.Point, color Color) {
	point := image.Pt(p.X/2, p.Y/4)
	self.CellMap[point] = Cell{
		self.CellMap[point].Rune | BRAILLE[p.Y%4][p.X%2],
		color,
	}
}

func (self *Canvas) SetLine(p0, p1 image.Point, color Color) {
	for _, p := range line(p0, p1) {
		self.SetPoint(p, color)
	}
}

func (self *Canvas) GetCells() map[image.Point]Cell {
	cellMap := make(map[image.Point]Cell)
	for point, cell := range self.CellMap {
		cellMap[point] = Cell{cell.Rune + BRAILLE_OFFSET, cell.Color}
	}
	return cellMap
}

func line(p0, p1 image.Point) []image.Point {
	points := []image.Point{}

	leftPoint, rightPoint := p0, p1
	if leftPoint.X > rightPoint.X {
		leftPoint, rightPoint = rightPoint, leftPoint
	}

	xDistance := absInt(leftPoint.X - rightPoint.X)
	yDistance := absInt(leftPoint.Y - rightPoint.Y)
	slope := float64(yDistance) / float64(xDistance)
	slopeSign := 1
	if rightPoint.Y < leftPoint.Y {
		slopeSign = -1
	}

	targetYCoordinate := float64(leftPoint.Y)
	currentYCoordinate := leftPoint.Y
	for i := leftPoint.X; i < rightPoint.X; i++ {
		points = append(points, image.Pt(i, currentYCoordinate))
		targetYCoordinate += (slope * float64(slopeSign))
		for currentYCoordinate != int(targetYCoordinate) {
			points = append(points, image.Pt(i, currentYCoordinate))
			currentYCoordinate += slopeSign
		}
	}

	return points
}

func absInt(x int) int {
	if x >= 0 {
		return x
	}
	return -x
}


================================================
FILE: events.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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"

	tb "github.com/nsf/termbox-go"
)

/*
List of events:
	mouse events:
		<MouseLeft> <MouseRight> <MouseMiddle>
		<MouseWheelUp> <MouseWheelDown>
	keyboard events:
		any uppercase or lowercase letter like j or J
		<C-d> etc
		<M-d> etc
		<Up> <Down> <Left> <Right>
		<Insert> <Delete> <Home> <End> <Previous> <Next>
		<Backspace> <Tab> <Enter> <Escape> <Space>
		<C-<Space>> etc
	terminal events:
        <Resize>

    keyboard events that do not work:
        <C-->
        <C-2> <C-~>
        <C-h>
        <C-i>
        <C-m>
        <C-[> <C-3>
        <C-\\>
        <C-]>
        <C-/> <C-_>
        <C-8>
*/

type EventType uint

const (
	KeyboardEvent EventType = iota
	MouseEvent
	ResizeEvent
)

type Event struct {
	Type    EventType
	ID      string
	Payload interface{}
}

// Mouse payload.
type Mouse struct {
	Drag bool
	X    int
	Y    int
}

// Resize payload.
type Resize struct {
	Width  int
	Height int
}

// PollEvents gets events from termbox, converts them, then sends them to each of its channels.
func PollEvents() <-chan Event {
	ch := make(chan Event)
	go func() {
		for {
			ch <- convertTermboxEvent(tb.PollEvent())
		}
	}()
	return ch
}

var keyboardMap = map[tb.Key]string{
	tb.KeyF1:         "<F1>",
	tb.KeyF2:         "<F2>",
	tb.KeyF3:         "<F3>",
	tb.KeyF4:         "<F4>",
	tb.KeyF5:         "<F5>",
	tb.KeyF6:         "<F6>",
	tb.KeyF7:         "<F7>",
	tb.KeyF8:         "<F8>",
	tb.KeyF9:         "<F9>",
	tb.KeyF10:        "<F10>",
	tb.KeyF11:        "<F11>",
	tb.KeyF12:        "<F12>",
	tb.KeyInsert:     "<Insert>",
	tb.KeyDelete:     "<Delete>",
	tb.KeyHome:       "<Home>",
	tb.KeyEnd:        "<End>",
	tb.KeyPgup:       "<PageUp>",
	tb.KeyPgdn:       "<PageDown>",
	tb.KeyArrowUp:    "<Up>",
	tb.KeyArrowDown:  "<Down>",
	tb.KeyArrowLeft:  "<Left>",
	tb.KeyArrowRight: "<Right>",

	tb.KeyCtrlSpace:  "<C-<Space>>", // tb.KeyCtrl2 tb.KeyCtrlTilde
	tb.KeyCtrlA:      "<C-a>",
	tb.KeyCtrlB:      "<C-b>",
	tb.KeyCtrlC:      "<C-c>",
	tb.KeyCtrlD:      "<C-d>",
	tb.KeyCtrlE:      "<C-e>",
	tb.KeyCtrlF:      "<C-f>",
	tb.KeyCtrlG:      "<C-g>",
	tb.KeyBackspace:  "<C-<Backspace>>", // tb.KeyCtrlH
	tb.KeyTab:        "<Tab>",           // tb.KeyCtrlI
	tb.KeyCtrlJ:      "<C-j>",
	tb.KeyCtrlK:      "<C-k>",
	tb.KeyCtrlL:      "<C-l>",
	tb.KeyEnter:      "<Enter>", // tb.KeyCtrlM
	tb.KeyCtrlN:      "<C-n>",
	tb.KeyCtrlO:      "<C-o>",
	tb.KeyCtrlP:      "<C-p>",
	tb.KeyCtrlQ:      "<C-q>",
	tb.KeyCtrlR:      "<C-r>",
	tb.KeyCtrlS:      "<C-s>",
	tb.KeyCtrlT:      "<C-t>",
	tb.KeyCtrlU:      "<C-u>",
	tb.KeyCtrlV:      "<C-v>",
	tb.KeyCtrlW:      "<C-w>",
	tb.KeyCtrlX:      "<C-x>",
	tb.KeyCtrlY:      "<C-y>",
	tb.KeyCtrlZ:      "<C-z>",
	tb.KeyEsc:        "<Escape>", // tb.KeyCtrlLsqBracket tb.KeyCtrl3
	tb.KeyCtrl4:      "<C-4>",    // tb.KeyCtrlBackslash
	tb.KeyCtrl5:      "<C-5>",    // tb.KeyCtrlRsqBracket
	tb.KeyCtrl6:      "<C-6>",
	tb.KeyCtrl7:      "<C-7>", // tb.KeyCtrlSlash tb.KeyCtrlUnderscore
	tb.KeySpace:      "<Space>",
	tb.KeyBackspace2: "<Backspace>", // tb.KeyCtrl8:
}

// convertTermboxKeyboardEvent converts a termbox keyboard event to a more friendly string format.
// Combines modifiers into the string instead of having them as additional fields in an event.
func convertTermboxKeyboardEvent(e tb.Event) Event {
	ID := "%s"
	if e.Mod == tb.ModAlt {
		ID = "<M-%s>"
	}

	if e.Ch != 0 {
		ID = fmt.Sprintf(ID, string(e.Ch))
	} else {
		converted, ok := keyboardMap[e.Key]
		if !ok {
			converted = ""
		}
		ID = fmt.Sprintf(ID, converted)
	}

	return Event{
		Type: KeyboardEvent,
		ID:   ID,
	}
}

var mouseButtonMap = map[tb.Key]string{
	tb.MouseLeft:      "<MouseLeft>",
	tb.MouseMiddle:    "<MouseMiddle>",
	tb.MouseRight:     "<MouseRight>",
	tb.MouseRelease:   "<MouseRelease>",
	tb.MouseWheelUp:   "<MouseWheelUp>",
	tb.MouseWheelDown: "<MouseWheelDown>",
}

func convertTermboxMouseEvent(e tb.Event) Event {
	converted, ok := mouseButtonMap[e.Key]
	if !ok {
		converted = "Unknown_Mouse_Button"
	}
	Drag := e.Mod == tb.ModMotion
	return Event{
		Type: MouseEvent,
		ID:   converted,
		Payload: Mouse{
			X:    e.MouseX,
			Y:    e.MouseY,
			Drag: Drag,
		},
	}
}

// convertTermboxEvent turns a termbox event into a termui event.
func convertTermboxEvent(e tb.Event) Event {
	if e.Type == tb.EventError {
		panic(e.Err)
	}
	switch e.Type {
	case tb.EventKey:
		return convertTermboxKeyboardEvent(e)
	case tb.EventMouse:
		return convertTermboxMouseEvent(e)
	case tb.EventResize:
		return Event{
			Type: ResizeEvent,
			ID:   "<Resize>",
			Payload: Resize{
				Width:  e.Width,
				Height: e.Height,
			},
		}
	}
	return Event{}
}


================================================
FILE: go.mod
================================================
module github.com/gizak/termui/v3

go 1.15

require (
	github.com/mattn/go-runewidth v0.0.15
	github.com/mitchellh/go-wordwrap v1.0.1
	github.com/nsf/termbox-go v1.1.1
	github.com/rivo/uniseg v0.4.4 // indirect
)


================================================
FILE: go.sum
================================================
github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=


================================================
FILE: grid.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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 gridItemType uint

const (
	col gridItemType = 0
	row gridItemType = 1
)

type Grid struct {
	Block
	Items []*GridItem
}

// GridItem represents either a Row or Column in a grid.
// Holds sizing information and either an []GridItems or a widget.
type GridItem struct {
	Type        gridItemType
	XRatio      float64
	YRatio      float64
	WidthRatio  float64
	HeightRatio float64
	Entry       interface{} // Entry.type == GridBufferer if IsLeaf else []GridItem
	IsLeaf      bool
	ratio       float64
}

func NewGrid() *Grid {
	g := &Grid{
		Block: *NewBlock(),
	}
	g.Border = false
	return g
}

// NewCol takes a height percentage and either a widget or a Row or Column
func NewCol(ratio float64, i ...interface{}) GridItem {
	_, ok := i[0].(Drawable)
	entry := i[0]
	if !ok {
		entry = i
	}
	return GridItem{
		Type:   col,
		Entry:  entry,
		IsLeaf: ok,
		ratio:  ratio,
	}
}

// NewRow takes a width percentage and either a widget or a Row or Column
func NewRow(ratio float64, i ...interface{}) GridItem {
	_, ok := i[0].(Drawable)
	entry := i[0]
	if !ok {
		entry = i
	}
	return GridItem{
		Type:   row,
		Entry:  entry,
		IsLeaf: ok,
		ratio:  ratio,
	}
}

// Set is used to add Columns and Rows to the grid.
// It recursively searches the GridItems, adding leaves to the grid and calculating the dimensions of the leaves.
func (self *Grid) Set(entries ...interface{}) {
	entry := GridItem{
		Type:   row,
		Entry:  entries,
		IsLeaf: false,
		ratio:  1.0,
	}
	self.setHelper(entry, 1.0, 1.0)
}

func (self *Grid) setHelper(item GridItem, parentWidthRatio, parentHeightRatio float64) {
	var HeightRatio float64
	var WidthRatio float64
	switch item.Type {
	case col:
		HeightRatio = 1.0
		WidthRatio = item.ratio
	case row:
		HeightRatio = item.ratio
		WidthRatio = 1.0
	}
	item.WidthRatio = parentWidthRatio * WidthRatio
	item.HeightRatio = parentHeightRatio * HeightRatio

	if item.IsLeaf {
		self.Items = append(self.Items, &item)
	} else {
		XRatio := 0.0
		YRatio := 0.0
		cols := false
		rows := false

		children := InterfaceSlice(item.Entry)

		for i := 0; i < len(children); i++ {
			if children[i] == nil {
				continue
			}
			child, _ := children[i].(GridItem)

			child.XRatio = item.XRatio + (item.WidthRatio * XRatio)
			child.YRatio = item.YRatio + (item.HeightRatio * YRatio)

			switch child.Type {
			case col:
				cols = true
				XRatio += child.ratio
				if rows {
					item.HeightRatio /= 2
				}
			case row:
				rows = true
				YRatio += child.ratio
				if cols {
					item.WidthRatio /= 2
				}
			}

			self.setHelper(child, item.WidthRatio, item.HeightRatio)
		}
	}
}

func (self *Grid) Draw(buf *Buffer) {
	width := float64(self.Dx()) + 1
	height := float64(self.Dy()) + 1

	for _, item := range self.Items {
		entry, _ := item.Entry.(Drawable)

		x := int(width*item.XRatio) + self.Min.X
		y := int(height*item.YRatio) + self.Min.Y
		w := int(width * item.WidthRatio)
		h := int(height * item.HeightRatio)

		if x+w > self.Dx() {
			w--
		}
		if y+h > self.Dy() {
			h--
		}

		entry.SetRect(x, y, x+w, y+h)

		entry.Lock()
		entry.Draw(buf)
		entry.Unlock()
	}
}


================================================
FILE: render.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import (
	"image"
	"sync"

	tb "github.com/nsf/termbox-go"
)

type Drawable interface {
	GetRect() image.Rectangle
	SetRect(int, int, int, int)
	Draw(*Buffer)
	sync.Locker
}

func Render(items ...Drawable) {
	for _, item := range items {
		buf := NewBuffer(item.GetRect())
		item.Lock()
		item.Draw(buf)
		item.Unlock()
		for point, cell := range buf.CellMap {
			if point.In(buf.Rectangle) {
				tb.SetCell(
					point.X, point.Y,
					cell.Rune,
					tb.Attribute(cell.Style.Fg+1)|tb.Attribute(cell.Style.Modifier), tb.Attribute(cell.Style.Bg+1),
				)
			}
		}
	}
	tb.Flush()
}


================================================
FILE: style.go
================================================
package termui

// Color is an integer from -1 to 255
// -1 = ColorClear
// 0-255 = Xterm colors
type Color int

// ColorClear clears the Fg or Bg color of a Style
const ColorClear Color = -1

// Basic terminal colors
const (
	ColorBlack   Color = 0
	ColorRed     Color = 1
	ColorGreen   Color = 2
	ColorYellow  Color = 3
	ColorBlue    Color = 4
	ColorMagenta Color = 5
	ColorCyan    Color = 6
	ColorWhite   Color = 7
)

type Modifier uint

const (
	// ModifierClear clears any modifiers
	ModifierClear     Modifier = 0
	ModifierBold      Modifier = 1 << 9
	ModifierUnderline Modifier = 1 << 10
	ModifierReverse   Modifier = 1 << 11
)

// Style represents the style of one terminal cell
type Style struct {
	Fg       Color
	Bg       Color
	Modifier Modifier
}

// StyleClear represents a default Style, with no colors or modifiers
var StyleClear = Style{
	Fg:       ColorClear,
	Bg:       ColorClear,
	Modifier: ModifierClear,
}

// NewStyle takes 1 to 3 arguments
// 1st argument = Fg
// 2nd argument = optional Bg
// 3rd argument = optional Modifier
func NewStyle(fg Color, args ...interface{}) Style {
	bg := ColorClear
	modifier := ModifierClear
	if len(args) >= 1 {
		bg = args[0].(Color)
	}
	if len(args) == 2 {
		modifier = args[1].(Modifier)
	}
	return Style{
		fg,
		bg,
		modifier,
	}
}


================================================
FILE: style_parser.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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"
)

const (
	tokenFg       = "fg"
	tokenBg       = "bg"
	tokenModifier = "mod"

	tokenItemSeparator  = ","
	tokenValueSeparator = ":"

	tokenBeginStyledText = '['
	tokenEndStyledText   = ']'

	tokenBeginStyle = '('
	tokenEndStyle   = ')'
)

type parserState uint

const (
	parserStateDefault parserState = iota
	parserStateStyleItems
	parserStateStyledText
)

// StyleParserColorMap can be modified to add custom color parsing to text
var StyleParserColorMap = map[string]Color{
	"red":     ColorRed,
	"blue":    ColorBlue,
	"black":   ColorBlack,
	"cyan":    ColorCyan,
	"yellow":  ColorYellow,
	"white":   ColorWhite,
	"clear":   ColorClear,
	"green":   ColorGreen,
	"magenta": ColorMagenta,
}

var modifierMap = map[string]Modifier{
	"bold":      ModifierBold,
	"underline": ModifierUnderline,
	"reverse":   ModifierReverse,
}

// readStyle translates an []rune like `fg:red,mod:bold,bg:white` to a style
func readStyle(runes []rune, defaultStyle Style) Style {
	style := defaultStyle
	split := strings.Split(string(runes), tokenItemSeparator)
	for _, item := range split {
		pair := strings.Split(item, tokenValueSeparator)
		if len(pair) == 2 {
			switch pair[0] {
			case tokenFg:
				style.Fg = StyleParserColorMap[pair[1]]
			case tokenBg:
				style.Bg = StyleParserColorMap[pair[1]]
			case tokenModifier:
				style.Modifier = modifierMap[pair[1]]
			}
		}
	}
	return style
}

// ParseStyles parses a string for embedded Styles and returns []Cell with the correct styling.
// Uses defaultStyle for any text without an embedded style.
// Syntax is of the form [text](fg:<color>,mod:<attribute>,bg:<color>).
// Ordering does not matter. All fields are optional.
func ParseStyles(s string, defaultStyle Style) []Cell {
	cells := []Cell{}
	runes := []rune(s)
	state := parserStateDefault
	styledText := []rune{}
	styleItems := []rune{}
	squareCount := 0

	reset := func() {
		styledText = []rune{}
		styleItems = []rune{}
		state = parserStateDefault
		squareCount = 0
	}

	rollback := func() {
		cells = append(cells, RunesToStyledCells(styledText, defaultStyle)...)
		cells = append(cells, RunesToStyledCells(styleItems, defaultStyle)...)
		reset()
	}

	// chop first and last runes
	chop := func(s []rune) []rune {
		return s[1 : len(s)-1]
	}

	for i, _rune := range runes {
		switch state {
		case parserStateDefault:
			if _rune == tokenBeginStyledText {
				state = parserStateStyledText
				squareCount = 1
				styledText = append(styledText, _rune)
			} else {
				cells = append(cells, Cell{_rune, defaultStyle})
			}
		case parserStateStyledText:
			switch {
			case squareCount == 0:
				switch _rune {
				case tokenBeginStyle:
					state = parserStateStyleItems
					styleItems = append(styleItems, _rune)
				default:
					rollback()
					switch _rune {
					case tokenBeginStyledText:
						state = parserStateStyledText
						squareCount = 1
						styleItems = append(styleItems, _rune)
					default:
						cells = append(cells, Cell{_rune, defaultStyle})
					}
				}
			case len(runes) == i+1:
				rollback()
				styledText = append(styledText, _rune)
			case _rune == tokenBeginStyledText:
				squareCount++
				styledText = append(styledText, _rune)
			case _rune == tokenEndStyledText:
				squareCount--
				styledText = append(styledText, _rune)
			default:
				styledText = append(styledText, _rune)
			}
		case parserStateStyleItems:
			styleItems = append(styleItems, _rune)
			if _rune == tokenEndStyle {
				style := readStyle(chop(styleItems), defaultStyle)
				cells = append(cells, RunesToStyledCells(chop(styledText), style)...)
				reset()
			} else if len(runes) == i+1 {
				rollback()
			}
		}
	}

	return cells
}


================================================
FILE: symbols.go
================================================
package termui

const (
	DOT      = '•'
	ELLIPSES = '…'

	UP_ARROW   = '▲'
	DOWN_ARROW = '▼'

	COLLAPSED = '+'
	EXPANDED  = '−'
)

var (
	BARS = [...]rune{' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}

	SHADED_BLOCKS = [...]rune{' ', '░', '▒', '▓', '█'}

	IRREGULAR_BLOCKS = [...]rune{
		' ', '▘', '▝', '▀', '▖', '▌', '▞', '▛',
		'▗', '▚', '▐', '▜', '▄', '▙', '▟', '█',
	}

	BRAILLE_OFFSET = '\u2800'
	BRAILLE        = [4][2]rune{
		{'\u0001', '\u0008'},
		{'\u0002', '\u0010'},
		{'\u0004', '\u0020'},
		{'\u0040', '\u0080'},
	}

	DOUBLE_BRAILLE = map[[2]int]rune{
		[2]int{0, 0}: '⣀',
		[2]int{0, 1}: '⡠',
		[2]int{0, 2}: '⡐',
		[2]int{0, 3}: '⡈',

		[2]int{1, 0}: '⢄',
		[2]int{1, 1}: '⠤',
		[2]int{1, 2}: '⠔',
		[2]int{1, 3}: '⠌',

		[2]int{2, 0}: '⢂',
		[2]int{2, 1}: '⠢',
		[2]int{2, 2}: '⠒',
		[2]int{2, 3}: '⠊',

		[2]int{3, 0}: '⢁',
		[2]int{3, 1}: '⠡',
		[2]int{3, 2}: '⠑',
		[2]int{3, 3}: '⠉',
	}

	SINGLE_BRAILLE_LEFT  = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
	SINGLE_BRAILLE_RIGHT = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
)


================================================
FILE: symbols_other.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

// +build !windows

package termui

const (
	TOP_LEFT     = '┌'
	TOP_RIGHT    = '┐'
	BOTTOM_LEFT  = '└'
	BOTTOM_RIGHT = '┘'

	VERTICAL_LINE   = '│'
	HORIZONTAL_LINE = '─'

	VERTICAL_LEFT   = '┤'
	VERTICAL_RIGHT  = '├'
	HORIZONTAL_UP   = '┴'
	HORIZONTAL_DOWN = '┬'

	QUOTA_LEFT  = '«'
	QUOTA_RIGHT = '»'

	VERTICAL_DASH   = '┊'
	HORIZONTAL_DASH = '┈'
)


================================================
FILE: symbols_windows.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

// +build windows

package termui

const (
	TOP_LEFT     = '+'
	TOP_RIGHT    = '+'
	BOTTOM_LEFT  = '+'
	BOTTOM_RIGHT = '+'

	VERTICAL_LINE   = '|'
	HORIZONTAL_LINE = '-'

	VERTICAL_LEFT   = '+'
	VERTICAL_RIGHT  = '+'
	HORIZONTAL_UP   = '+'
	HORIZONTAL_DOWN = '+'

	QUOTA_LEFT  = '<'
	QUOTA_RIGHT = '>'

	VERTICAL_DASH   = '|'
	HORIZONTAL_DASH = '-'
)


================================================
FILE: theme.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.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

var StandardColors = []Color{
	ColorRed,
	ColorGreen,
	ColorYellow,
	ColorBlue,
	ColorMagenta,
	ColorCyan,
	ColorWhite,
}

var StandardStyles = []Style{
	NewStyle(ColorRed),
	NewStyle(ColorGreen),
	NewStyle(ColorYellow),
	NewStyle(ColorBlue),
	NewStyle(ColorMagenta),
	NewStyle(ColorCyan),
	NewStyle(ColorWhite),
}

type RootTheme struct {
	Default Style

	Block BlockTheme

	BarChart        BarChartTheme
	Gauge           GaugeTheme
	Plot            PlotTheme
	List            ListTheme
	Tree            TreeTheme
	Paragraph       ParagraphTheme
	PieChart        PieChartTheme
	Sparkline       SparklineTheme
	StackedBarChart StackedBarChartTheme
	Tab             TabTheme
	Table           TableTheme
}

type BlockTheme struct {
	Title  Style
	Border Style
}

type BarChartTheme struct {
	Bars   []Color
	Nums   []Style
	Labels []Style
}

type GaugeTheme struct {
	Bar   Color
	Label Style
}

type PlotTheme struct {
	Lines []Color
	Axes  Color
}

type ListTheme struct {
	Text Style
}

type TreeTheme struct {
	Text      Style
	Collapsed rune
	Expanded  rune
}

type ParagraphTheme struct {
	Text Style
}

type PieChartTheme struct {
	Slices []Color
}

type SparklineTheme struct {
	Title Style
	Line  Color
}

type StackedBarChartTheme struct {
	Bars   []Color
	Nums   []Style
	Labels []Style
}

type TabTheme struct {
	Active   Style
	Inactive Style
}

type TableTheme struct {
	Text Style
}

// Theme holds the default Styles and Colors for all widgets.
// You can set default widget Styles by modifying the Theme before creating the widgets.
var Theme = RootTheme{
	Default: NewStyle(ColorWhite),

	Block: BlockTheme{
		Title:  NewStyle(ColorWhite),
		Border: NewStyle(ColorWhite),
	},

	BarChart: BarChartTheme{
		Bars:   StandardColors,
		Nums:   StandardStyles,
		Labels: StandardStyles,
	},

	Paragraph: ParagraphTheme{
		Text: NewStyle(ColorWhite),
	},

	PieChart: PieChartTheme{
		Slices: StandardColors,
	},

	List: ListTheme{
		Text: NewStyle(ColorWhite),
	},

	Tree: TreeTheme{
		Text:      NewStyle(ColorWhite),
		Collapsed: COLLAPSED,
		Expanded:  EXPANDED,
	},

	StackedBarChart: StackedBarChartTheme{
		Bars:   StandardColors,
		Nums:   StandardStyles,
		Labels: StandardStyles,
	},

	Gauge: GaugeTheme{
		Bar:   ColorWhite,
		Label: NewStyle(ColorWhite),
	},

	Sparkline: SparklineTheme{
		Title: NewStyle(ColorWhite),
		Line:  ColorWhite,
	},

	Plot: PlotTheme{
		Lines: StandardColors,
		Axes:  ColorWhite,
	},

	Table: TableTheme{
		Text: NewStyle(ColorWhite),
	},

	Tab: TabTheme{
		Active:   NewStyle(ColorRed),
		Inactive: NewStyle(ColorWhite),
	},
}


================================================
FILE: utils.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import (
	"fmt"
	"math"
	"reflect"

	rw "github.com/mattn/go-runewidth"
	wordwrap "github.com/mitchellh/go-wordwrap"
)

// InterfaceSlice takes an []interface{} represented as an interface{} and converts it
// https://stackoverflow.com/questions/12753805/type-converting-slices-of-interfaces-in-go
func InterfaceSlice(slice interface{}) []interface{} {
	s := reflect.ValueOf(slice)
	if s.Kind() != reflect.Slice {
		panic("InterfaceSlice() given a non-slice type")
	}

	ret := make([]interface{}, s.Len())

	for i := 0; i < s.Len(); i++ {
		ret[i] = s.Index(i).Interface()
	}

	return ret
}

// TrimString trims a string to a max length and adds '…' to the end if it was trimmed.
func TrimString(s string, w int) string {
	if w <= 0 {
		return ""
	}
	if rw.StringWidth(s) > w {
		return rw.Truncate(s, w, string(ELLIPSES))
	}
	return s
}

func SelectColor(colors []Color, index int) Color {
	return colors[index%len(colors)]
}

func SelectStyle(styles []Style, index int) Style {
	return styles[index%len(styles)]
}

// Math ------------------------------------------------------------------------

func SumIntSlice(slice []int) int {
	sum := 0
	for _, val := range slice {
		sum += val
	}
	return sum
}

func SumFloat64Slice(data []float64) float64 {
	sum := 0.0
	for _, v := range data {
		sum += v
	}
	return sum
}

func GetMaxIntFromSlice(slice []int) (int, error) {
	if len(slice) == 0 {
		return 0, fmt.Errorf("cannot get max value from empty slice")
	}
	var max int
	for _, val := range slice {
		if val > max {
			max = val
		}
	}
	return max, nil
}

func GetMaxFloat64FromSlice(slice []float64) (float64, error) {
	if len(slice) == 0 {
		return 0, fmt.Errorf("cannot get max value from empty slice")
	}
	var max float64
	for _, val := range slice {
		if val > max {
			max = val
		}
	}
	return max, nil
}

func GetMaxFloat64From2dSlice(slices [][]float64) (float64, error) {
	if len(slices) == 0 {
		return 0, fmt.Errorf("cannot get max value from empty slice")
	}
	var max float64
	for _, slice := range slices {
		for _, val := range slice {
			if val > max {
				max = val
			}
		}
	}
	return max, nil
}

func RoundFloat64(x float64) float64 {
	return math.Floor(x + 0.5)
}

func FloorFloat64(x float64) float64 {
	return math.Floor(x)
}

func AbsInt(x int) int {
	if x >= 0 {
		return x
	}
	return -x
}

func MinFloat64(x, y float64) float64 {
	if x < y {
		return x
	}
	return y
}

func MaxFloat64(x, y float64) float64 {
	if x > y {
		return x
	}
	return y
}

func MaxInt(x, y int) int {
	if x > y {
		return x
	}
	return y
}

func MinInt(x, y int) int {
	if x < y {
		return x
	}
	return y
}

// []Cell ----------------------------------------------------------------------

// WrapCells takes []Cell and inserts Cells containing '\n' wherever a linebreak should go.
func WrapCells(cells []Cell, width uint) []Cell {
	str := CellsToString(cells)
	wrapped := wordwrap.WrapString(str, width)
	wrappedCells := []Cell{}
	i := 0
	for _, _rune := range wrapped {
		if _rune == '\n' {
			wrappedCells = append(wrappedCells, Cell{_rune, StyleClear})
		} else {
			wrappedCells = append(wrappedCells, Cell{_rune, cells[i].Style})
		}
		i++
	}
	return wrappedCells
}

func RunesToStyledCells(runes []rune, style Style) []Cell {
	cells := []Cell{}
	for _, _rune := range runes {
		cells = append(cells, Cell{_rune, style})
	}
	return cells
}

func CellsToString(cells []Cell) string {
	runes := make([]rune, len(cells))
	for i, cell := range cells {
		runes[i] = cell.Rune
	}
	return string(runes)
}

func TrimCells(cells []Cell, w int) []Cell {
	s := CellsToString(cells)
	s = TrimString(s, w)
	runes := []rune(s)
	newCells := []Cell{}
	for i, r := range runes {
		newCells = append(newCells, Cell{r, cells[i].Style})
	}
	return newCells
}

func SplitCells(cells []Cell, r rune) [][]Cell {
	splitCells := [][]Cell{}
	temp := []Cell{}
	for _, cell := range cells {
		if cell.Rune == r {
			splitCells = append(splitCells, temp)
			temp = []Cell{}
		} else {
			temp = append(temp, cell)
		}
	}
	if len(temp) > 0 {
		splitCells = append(splitCells, temp)
	}
	return splitCells
}

type CellWithX struct {
	X    int
	Cell Cell
}

func BuildCellWithXArray(cells []Cell) []CellWithX {
	cellWithXArray := make([]CellWithX, len(cells))
	index := 0
	for i, cell := range cells {
		cellWithXArray[i] = CellWithX{X: index, Cell: cell}
		index += rw.RuneWidth(cell.Rune)
	}
	return cellWithXArray
}


================================================
FILE: widgets/barchart.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"fmt"
	"image"

	rw "github.com/mattn/go-runewidth"

	. "github.com/gizak/termui/v3"
)

type BarChart struct {
	Block
	BarColors    []Color
	LabelStyles  []Style
	NumStyles    []Style // only Fg and Modifier are used
	NumFormatter func(float64) string
	Data         []float64
	Labels       []string
	BarWidth     int
	BarGap       int
	MaxVal       float64
}

func NewBarChart() *BarChart {
	return &BarChart{
		Block:        *NewBlock(),
		BarColors:    Theme.BarChart.Bars,
		NumStyles:    Theme.BarChart.Nums,
		LabelStyles:  Theme.BarChart.Labels,
		NumFormatter: func(n float64) string { return fmt.Sprint(n) },
		BarGap:       1,
		BarWidth:     3,
	}
}

func (self *BarChart) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	maxVal := self.MaxVal
	if maxVal == 0 {
		maxVal, _ = GetMaxFloat64FromSlice(self.Data)
	}

	barXCoordinate := self.Inner.Min.X

	for i, data := range self.Data {
		if data > 0 {
			// draw bar
			height := int((data / maxVal) * float64(self.Inner.Dy()-1))
			for x := barXCoordinate; x < MinInt(barXCoordinate+self.BarWidth, self.Inner.Max.X); x++ {
				for y := self.Inner.Max.Y - 2; y > (self.Inner.Max.Y-2)-height; y-- {
					c := NewCell(' ', NewStyle(ColorClear, SelectColor(self.BarColors, i)))
					buf.SetCell(c, image.Pt(x, y))
				}
			}
		}
		// draw label
		if i < len(self.Labels) {
			labelXCoordinate := barXCoordinate +
				int((float64(self.BarWidth) / 2)) -
				int((float64(rw.StringWidth(self.Labels[i])) / 2))
			buf.SetString(
				self.Labels[i],
				SelectStyle(self.LabelStyles, i),
				image.Pt(labelXCoordinate, self.Inner.Max.Y-1),
			)
		}

		// draw number
		numberXCoordinate := barXCoordinate + int((float64(self.BarWidth) / 2))
		if numberXCoordinate <= self.Inner.Max.X {
			buf.SetString(
				self.NumFormatter(data),
				NewStyle(
					SelectStyle(self.NumStyles, i+1).Fg,
					SelectColor(self.BarColors, i),
					SelectStyle(self.NumStyles, i+1).Modifier,
				),
				image.Pt(numberXCoordinate, self.Inner.Max.Y-2),
			)
		}

		barXCoordinate += (self.BarWidth + self.BarGap)
	}
}


================================================
FILE: widgets/gauge.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"fmt"
	"image"

	. "github.com/gizak/termui/v3"
)

type Gauge struct {
	Block
	Percent    int
	BarColor   Color
	Label      string
	LabelStyle Style
}

func NewGauge() *Gauge {
	return &Gauge{
		Block:      *NewBlock(),
		BarColor:   Theme.Gauge.Bar,
		LabelStyle: Theme.Gauge.Label,
	}
}

func (self *Gauge) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	label := self.Label
	if label == "" {
		label = fmt.Sprintf("%d%%", self.Percent)
	}

	// plot bar
	barWidth := int((float64(self.Percent) / 100) * float64(self.Inner.Dx()))
	buf.Fill(
		NewCell(' ', NewStyle(ColorClear, self.BarColor)),
		image.Rect(self.Inner.Min.X, self.Inner.Min.Y, self.Inner.Min.X+barWidth, self.Inner.Max.Y),
	)

	// plot label
	labelXCoordinate := self.Inner.Min.X + (self.Inner.Dx() / 2) - int(float64(len(label))/2)
	labelYCoordinate := self.Inner.Min.Y + ((self.Inner.Dy() - 1) / 2)
	if labelYCoordinate < self.Inner.Max.Y {
		for i, char := range label {
			style := self.LabelStyle
			if labelXCoordinate+i+1 <= self.Inner.Min.X+barWidth {
				style = NewStyle(self.BarColor, ColorClear, ModifierReverse)
			}
			buf.SetCell(NewCell(char, style), image.Pt(labelXCoordinate+i, labelYCoordinate))
		}
	}
}


================================================
FILE: widgets/image.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"
	"image/color"

	. "github.com/gizak/termui/v3"
)

type Image struct {
	Block
	Image               image.Image
	Monochrome          bool
	MonochromeThreshold uint8
	MonochromeInvert    bool
}

func NewImage(img image.Image) *Image {
	return &Image{
		Block:               *NewBlock(),
		MonochromeThreshold: 128,
		Image:               img,
	}
}

func (self *Image) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	if self.Image == nil {
		return
	}

	bufWidth := self.Inner.Dx()
	bufHeight := self.Inner.Dy()
	imageWidth := self.Image.Bounds().Dx()
	imageHeight := self.Image.Bounds().Dy()

	if self.Monochrome {
		if bufWidth > imageWidth/2 {
			bufWidth = imageWidth / 2
		}
		if bufHeight > imageHeight/2 {
			bufHeight = imageHeight / 2
		}
		for bx := 0; bx < bufWidth; bx++ {
			for by := 0; by < bufHeight; by++ {
				ul := self.colorAverage(
					2*bx*imageWidth/bufWidth/2,
					(2*bx+1)*imageWidth/bufWidth/2,
					2*by*imageHeight/bufHeight/2,
					(2*by+1)*imageHeight/bufHeight/2,
				)
				ur := self.colorAverage(
					(2*bx+1)*imageWidth/bufWidth/2,
					(2*bx+2)*imageWidth/bufWidth/2,
					2*by*imageHeight/bufHeight/2,
					(2*by+1)*imageHeight/bufHeight/2,
				)
				ll := self.colorAverage(
					2*bx*imageWidth/bufWidth/2,
					(2*bx+1)*imageWidth/bufWidth/2,
					(2*by+1)*imageHeight/bufHeight/2,
					(2*by+2)*imageHeight/bufHeight/2,
				)
				lr := self.colorAverage(
					(2*bx+1)*imageWidth/bufWidth/2,
					(2*bx+2)*imageWidth/bufWidth/2,
					(2*by+1)*imageHeight/bufHeight/2,
					(2*by+2)*imageHeight/bufHeight/2,
				)
				buf.SetCell(
					NewCell(blocksChar(ul, ur, ll, lr, self.MonochromeThreshold, self.MonochromeInvert)),
					image.Pt(self.Inner.Min.X+bx, self.Inner.Min.Y+by),
				)
			}
		}
	} else {
		if bufWidth > imageWidth {
			bufWidth = imageWidth
		}
		if bufHeight > imageHeight {
			bufHeight = imageHeight
		}
		for bx := 0; bx < bufWidth; bx++ {
			for by := 0; by < bufHeight; by++ {
				c := self.colorAverage(
					bx*imageWidth/bufWidth,
					(bx+1)*imageWidth/bufWidth,
					by*imageHeight/bufHeight,
					(by+1)*imageHeight/bufHeight,
				)
				buf.SetCell(
					NewCell(c.ch(), NewStyle(c.fgColor(), ColorBlack)),
					image.Pt(self.Inner.Min.X+bx, self.Inner.Min.Y+by),
				)
			}
		}
	}
}

func (self *Image) colorAverage(x0, x1, y0, y1 int) colorAverager {
	var c colorAverager
	for x := x0; x < x1; x++ {
		for y := y0; y < y1; y++ {
			c = c.add(
				self.Image.At(
					x+self.Image.Bounds().Min.X,
					y+self.Image.Bounds().Min.Y,
				),
			)
		}
	}
	return c
}

type colorAverager struct {
	rsum, gsum, bsum, asum, count uint64
}

func (self colorAverager) add(col color.Color) colorAverager {
	r, g, b, a := col.RGBA()
	return colorAverager{
		rsum:  self.rsum + uint64(r),
		gsum:  self.gsum + uint64(g),
		bsum:  self.bsum + uint64(b),
		asum:  self.asum + uint64(a),
		count: self.count + 1,
	}
}

func (self colorAverager) RGBA() (uint32, uint32, uint32, uint32) {
	if self.count == 0 {
		return 0, 0, 0, 0
	}
	return uint32(self.rsum/self.count) & 0xffff,
		uint32(self.gsum/self.count) & 0xffff,
		uint32(self.bsum/self.count) & 0xffff,
		uint32(self.asum/self.count) & 0xffff
}

func (self colorAverager) fgColor() Color {
	return palette.Convert(self).(paletteColor).attribute
}

func (self colorAverager) ch() rune {
	gray := color.GrayModel.Convert(self).(color.Gray).Y
	switch {
	case gray < 51:
		return SHADED_BLOCKS[0]
	case gray < 102:
		return SHADED_BLOCKS[1]
	case gray < 153:
		return SHADED_BLOCKS[2]
	case gray < 204:
		return SHADED_BLOCKS[3]
	default:
		return SHADED_BLOCKS[4]
	}
}

func (self colorAverager) monochrome(threshold uint8, invert bool) bool {
	return self.count != 0 && (color.GrayModel.Convert(self).(color.Gray).Y < threshold != invert)
}

type paletteColor struct {
	rgba      color.RGBA
	attribute Color
}

func (self paletteColor) RGBA() (uint32, uint32, uint32, uint32) {
	return self.rgba.RGBA()
}

var palette = color.Palette([]color.Color{
	paletteColor{color.RGBA{0, 0, 0, 255}, ColorBlack},
	paletteColor{color.RGBA{255, 0, 0, 255}, ColorRed},
	paletteColor{color.RGBA{0, 255, 0, 255}, ColorGreen},
	paletteColor{color.RGBA{255, 255, 0, 255}, ColorYellow},
	paletteColor{color.RGBA{0, 0, 255, 255}, ColorBlue},
	paletteColor{color.RGBA{255, 0, 255, 255}, ColorMagenta},
	paletteColor{color.RGBA{0, 255, 255, 255}, ColorCyan},
	paletteColor{color.RGBA{255, 255, 255, 255}, ColorWhite},
})

func blocksChar(ul, ur, ll, lr colorAverager, threshold uint8, invert bool) rune {
	index := 0
	if ul.monochrome(threshold, invert) {
		index |= 1
	}
	if ur.monochrome(threshold, invert) {
		index |= 2
	}
	if ll.monochrome(threshold, invert) {
		index |= 4
	}
	if lr.monochrome(threshold, invert) {
		index |= 8
	}
	return IRREGULAR_BLOCKS[index]
}


================================================
FILE: widgets/list.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	rw "github.com/mattn/go-runewidth"

	. "github.com/gizak/termui/v3"
)

type List struct {
	Block
	Rows             []string
	WrapText         bool
	TextStyle        Style
	SelectedRow      int
	topRow           int
	SelectedRowStyle Style
}

func NewList() *List {
	return &List{
		Block:            *NewBlock(),
		TextStyle:        Theme.List.Text,
		SelectedRowStyle: Theme.List.Text,
	}
}

func (self *List) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	point := self.Inner.Min

	// adjusts view into widget
	if self.SelectedRow >= self.Inner.Dy()+self.topRow {
		self.topRow = self.SelectedRow - self.Inner.Dy() + 1
	} else if self.SelectedRow < self.topRow {
		self.topRow = self.SelectedRow
	}

	// draw rows
	for row := self.topRow; row < len(self.Rows) && point.Y < self.Inner.Max.Y; row++ {
		cells := ParseStyles(self.Rows[row], self.TextStyle)
		if self.WrapText {
			cells = WrapCells(cells, uint(self.Inner.Dx()))
		}
		for j := 0; j < len(cells) && point.Y < self.Inner.Max.Y; j++ {
			style := cells[j].Style
			if row == self.SelectedRow {
				style = self.SelectedRowStyle
			}
			if cells[j].Rune == '\n' {
				point = image.Pt(self.Inner.Min.X, point.Y+1)
			} else {
				if point.X+1 == self.Inner.Max.X+1 && len(cells) > self.Inner.Dx() {
					buf.SetCell(NewCell(ELLIPSES, style), point.Add(image.Pt(-1, 0)))
					break
				} else {
					buf.SetCell(NewCell(cells[j].Rune, style), point)
					point = point.Add(image.Pt(rw.RuneWidth(cells[j].Rune), 0))
				}
			}
		}
		point = image.Pt(self.Inner.Min.X, point.Y+1)
	}

	// draw UP_ARROW if needed
	if self.topRow > 0 {
		buf.SetCell(
			NewCell(UP_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Min.Y),
		)
	}

	// draw DOWN_ARROW if needed
	if len(self.Rows) > int(self.topRow)+self.Inner.Dy() {
		buf.SetCell(
			NewCell(DOWN_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Max.Y-1),
		)
	}
}

// ScrollAmount scrolls by amount given. If amount is < 0, then scroll up.
// There is no need to set self.topRow, as this will be set automatically when drawn,
// since if the selected item is off screen then the topRow variable will change accordingly.
func (self *List) ScrollAmount(amount int) {
	if len(self.Rows)-int(self.SelectedRow) <= amount {
		self.SelectedRow = len(self.Rows) - 1
	} else if int(self.SelectedRow)+amount < 0 {
		self.SelectedRow = 0
	} else {
		self.SelectedRow += amount
	}
}

func (self *List) ScrollUp() {
	self.ScrollAmount(-1)
}

func (self *List) ScrollDown() {
	self.ScrollAmount(1)
}

func (self *List) ScrollPageUp() {
	// If an item is selected below top row, then go to the top row.
	if self.SelectedRow > self.topRow {
		self.SelectedRow = self.topRow
	} else {
		self.ScrollAmount(-self.Inner.Dy())
	}
}

func (self *List) ScrollPageDown() {
	self.ScrollAmount(self.Inner.Dy())
}

func (self *List) ScrollHalfPageUp() {
	self.ScrollAmount(-int(FloorFloat64(float64(self.Inner.Dy()) / 2)))
}

func (self *List) ScrollHalfPageDown() {
	self.ScrollAmount(int(FloorFloat64(float64(self.Inner.Dy()) / 2)))
}

func (self *List) ScrollTop() {
	self.SelectedRow = 0
}

func (self *List) ScrollBottom() {
	self.SelectedRow = len(self.Rows) - 1
}


================================================
FILE: widgets/paragraph.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	. "github.com/gizak/termui/v3"
)

type Paragraph struct {
	Block
	Text      string
	TextStyle Style
	WrapText  bool
}

func NewParagraph() *Paragraph {
	return &Paragraph{
		Block:     *NewBlock(),
		TextStyle: Theme.Paragraph.Text,
		WrapText:  true,
	}
}

func (self *Paragraph) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	cells := ParseStyles(self.Text, self.TextStyle)
	if self.WrapText {
		cells = WrapCells(cells, uint(self.Inner.Dx()))
	}

	rows := SplitCells(cells, '\n')

	for y, row := range rows {
		if y+self.Inner.Min.Y >= self.Inner.Max.Y {
			break
		}
		row = TrimCells(row, self.Inner.Dx())
		for _, cx := range BuildCellWithXArray(row) {
			x, cell := cx.X, cx.Cell
			buf.SetCell(cell, image.Pt(x, y).Add(self.Inner.Min))
		}
	}
}


================================================
FILE: widgets/piechart.go
================================================
package widgets

import (
	"image"
	"math"

	. "github.com/gizak/termui/v3"
)

const (
	piechartOffsetUp = -.5 * math.Pi // the northward angle
	resolutionFactor = .0001         // circle resolution: precision vs. performance
	fullCircle       = 2.0 * math.Pi // the full circle angle
	xStretch         = 2.0           // horizontal adjustment
)

// PieChartLabel callback
type PieChartLabel func(dataIndex int, currentValue float64) string

type PieChart struct {
	Block
	Data           []float64     // list of data items
	Colors         []Color       // colors to by cycled through
	LabelFormatter PieChartLabel // callback function for labels
	AngleOffset    float64       // which angle to start drawing at? (see piechartOffsetUp)
}

// NewPieChart Creates a new pie chart with reasonable defaults and no labels.
func NewPieChart() *PieChart {
	return &PieChart{
		Block:       *NewBlock(),
		Colors:      Theme.PieChart.Slices,
		AngleOffset: piechartOffsetUp,
	}
}

func (self *PieChart) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	center := self.Inner.Min.Add(self.Inner.Size().Div(2))
	radius := MinFloat64(float64(self.Inner.Dx()/2/xStretch), float64(self.Inner.Dy()/2))

	// compute slice sizes
	sum := SumFloat64Slice(self.Data)
	sliceSizes := make([]float64, len(self.Data))
	for i, v := range self.Data {
		sliceSizes[i] = v / sum * fullCircle
	}

	borderCircle := &circle{center, radius}
	middleCircle := circle{Point: center, radius: radius / 2.0}

	// draw sectors
	phi := self.AngleOffset
	for i, size := range sliceSizes {
		for j := 0.0; j < size; j += resolutionFactor {
			borderPoint := borderCircle.at(phi + j)
			line := line{P1: center, P2: borderPoint}
			line.draw(NewCell(SHADED_BLOCKS[1], NewStyle(SelectColor(self.Colors, i))), buf)
		}
		phi += size
	}

	// draw labels
	if self.LabelFormatter != nil {
		phi = self.AngleOffset
		for i, size := range sliceSizes {
			labelPoint := middleCircle.at(phi + size/2.0)
			if len(self.Data) == 1 {
				labelPoint = center
			}
			buf.SetString(
				self.LabelFormatter(i, self.Data[i]),
				NewStyle(SelectColor(self.Colors, i)),
				image.Pt(labelPoint.X, labelPoint.Y),
			)
			phi += size
		}
	}
}

type circle struct {
	image.Point
	radius float64
}

// computes the point at a given angle phi
func (self circle) at(phi float64) image.Point {
	x := self.X + int(RoundFloat64(xStretch*self.radius*math.Cos(phi)))
	y := self.Y + int(RoundFloat64(self.radius*math.Sin(phi)))
	return image.Point{X: x, Y: y}
}

// computes the perimeter of a circle
func (self circle) perimeter() float64 {
	return 2.0 * math.Pi * self.radius
}

// a line between two points
type line struct {
	P1, P2 image.Point
}

// draws the line
func (self line) draw(cell Cell, buf *Buffer) {
	isLeftOf := func(p1, p2 image.Point) bool {
		return p1.X <= p2.X
	}
	isTopOf := func(p1, p2 image.Point) bool {
		return p1.Y <= p2.Y
	}
	p1, p2 := self.P1, self.P2
	buf.SetCell(NewCell('*', cell.Style), self.P2)
	width, height := self.size()
	if width > height { // paint left to right
		if !isLeftOf(p1, p2) {
			p1, p2 = p2, p1
		}
		flip := 1.0
		if !isTopOf(p1, p2) {
			flip = -1.0
		}
		for x := p1.X; x <= p2.X; x++ {
			ratio := float64(height) / float64(width)
			factor := float64(x - p1.X)
			y := ratio * factor * flip
			buf.SetCell(cell, image.Pt(x, int(RoundFloat64(y))+p1.Y))
		}
	} else { // paint top to bottom
		if !isTopOf(p1, p2) {
			p1, p2 = p2, p1
		}
		flip := 1.0
		if !isLeftOf(p1, p2) {
			flip = -1.0
		}
		for y := p1.Y; y <= p2.Y; y++ {
			ratio := float64(width) / float64(height)
			factor := float64(y - p1.Y)
			x := ratio * factor * flip
			buf.SetCell(cell, image.Pt(int(RoundFloat64(x))+p1.X, y))
		}
	}
}

// width and height of a line
func (self line) size() (w, h int) {
	return AbsInt(self.P2.X - self.P1.X), AbsInt(self.P2.Y - self.P1.Y)
}


================================================
FILE: widgets/plot.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"fmt"
	"image"

	. "github.com/gizak/termui/v3"
)

// Plot has two modes: line(default) and scatter.
// Plot also has two marker types: braille(default) and dot.
// A single braille character is a 2x4 grid of dots, so using braille
// gives 2x X resolution and 4x Y resolution over dot mode.
type Plot struct {
	Block

	Data       [][]float64
	DataLabels []string
	MaxVal     float64

	LineColors []Color
	AxesColor  Color // TODO
	ShowAxes   bool

	Marker          PlotMarker
	DotMarkerRune   rune
	PlotType        PlotType
	HorizontalScale int
	DrawDirection   DrawDirection // TODO
}

const (
	xAxisLabelsHeight = 1
	yAxisLabelsWidth  = 4
	xAxisLabelsGap    = 2
	yAxisLabelsGap    = 1
)

type PlotType uint

const (
	LineChart PlotType = iota
	ScatterPlot
)

type PlotMarker uint

const (
	MarkerBraille PlotMarker = iota
	MarkerDot
)

type DrawDirection uint

const (
	DrawLeft DrawDirection = iota
	DrawRight
)

func NewPlot() *Plot {
	return &Plot{
		Block:           *NewBlock(),
		LineColors:      Theme.Plot.Lines,
		AxesColor:       Theme.Plot.Axes,
		Marker:          MarkerBraille,
		DotMarkerRune:   DOT,
		Data:            [][]float64{},
		HorizontalScale: 1,
		DrawDirection:   DrawRight,
		ShowAxes:        true,
		PlotType:        LineChart,
	}
}

func (self *Plot) renderBraille(buf *Buffer, drawArea image.Rectangle, maxVal float64) {
	canvas := NewCanvas()
	canvas.Rectangle = drawArea

	switch self.PlotType {
	case ScatterPlot:
		for i, line := range self.Data {
			for j, val := range line {
				height := int((val / maxVal) * float64(drawArea.Dy()-1))
				canvas.SetPoint(
					image.Pt(
						(drawArea.Min.X+(j*self.HorizontalScale))*2,
						(drawArea.Max.Y-height-1)*4,
					),
					SelectColor(self.LineColors, i),
				)
			}
		}
	case LineChart:
		for i, line := range self.Data {
			previousHeight := int((line[1] / maxVal) * float64(drawArea.Dy()-1))
			for j, val := range line[1:] {
				height := int((val / maxVal) * float64(drawArea.Dy()-1))
				canvas.SetLine(
					image.Pt(
						(drawArea.Min.X+(j*self.HorizontalScale))*2,
						(drawArea.Max.Y-previousHeight-1)*4,
					),
					image.Pt(
						(drawArea.Min.X+((j+1)*self.HorizontalScale))*2,
						(drawArea.Max.Y-height-1)*4,
					),
					SelectColor(self.LineColors, i),
				)
				previousHeight = height
			}
		}
	}

	canvas.Draw(buf)
}

func (self *Plot) renderDot(buf *Buffer, drawArea image.Rectangle, maxVal float64) {
	switch self.PlotType {
	case ScatterPlot:
		for i, line := range self.Data {
			for j, val := range line {
				height := int((val / maxVal) * float64(drawArea.Dy()-1))
				point := image.Pt(drawArea.Min.X+(j*self.HorizontalScale), drawArea.Max.Y-1-height)
				if point.In(drawArea) {
					buf.SetCell(
						NewCell(self.DotMarkerRune, NewStyle(SelectColor(self.LineColors, i))),
						point,
					)
				}
			}
		}
	case LineChart:
		for i, line := range self.Data {
			for j := 0; j < len(line) && j*self.HorizontalScale < drawArea.Dx(); j++ {
				val := line[j]
				height := int((val / maxVal) * float64(drawArea.Dy()-1))
				buf.SetCell(
					NewCell(self.DotMarkerRune, NewStyle(SelectColor(self.LineColors, i))),
					image.Pt(drawArea.Min.X+(j*self.HorizontalScale), drawArea.Max.Y-1-height),
				)
			}
		}
	}
}

func (self *Plot) plotAxes(buf *Buffer, maxVal float64) {
	// draw origin cell
	buf.SetCell(
		NewCell(BOTTOM_LEFT, NewStyle(ColorWhite)),
		image.Pt(self.Inner.Min.X+yAxisLabelsWidth, self.Inner.Max.Y-xAxisLabelsHeight-1),
	)
	// draw x axis line
	for i := yAxisLabelsWidth + 1; i < self.Inner.Dx(); i++ {
		buf.SetCell(
			NewCell(HORIZONTAL_DASH, NewStyle(ColorWhite)),
			image.Pt(i+self.Inner.Min.X, self.Inner.Max.Y-xAxisLabelsHeight-1),
		)
	}
	// draw y axis line
	for i := 0; i < self.Inner.Dy()-xAxisLabelsHeight-1; i++ {
		buf.SetCell(
			NewCell(VERTICAL_DASH, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Min.X+yAxisLabelsWidth, i+self.Inner.Min.Y),
		)
	}
	// draw x axis labels
	// draw 0
	buf.SetString(
		"0",
		NewStyle(ColorWhite),
		image.Pt(self.Inner.Min.X+yAxisLabelsWidth, self.Inner.Max.Y-1),
	)
	// draw rest
	for x := self.Inner.Min.X + yAxisLabelsWidth + (xAxisLabelsGap)*self.HorizontalScale + 1; x < self.Inner.Max.X-1; {
		label := fmt.Sprintf(
			"%d",
			(x-(self.Inner.Min.X+yAxisLabelsWidth)-1)/(self.HorizontalScale)+1,
		)
		buf.SetString(
			label,
			NewStyle(ColorWhite),
			image.Pt(x, self.Inner.Max.Y-1),
		)
		x += (len(label) + xAxisLabelsGap) * self.HorizontalScale
	}
	// draw y axis labels
	verticalScale := maxVal / float64(self.Inner.Dy()-xAxisLabelsHeight-1)
	for i := 0; i*(yAxisLabelsGap+1) < self.Inner.Dy()-1; i++ {
		buf.SetString(
			fmt.Sprintf("%.2f", float64(i)*verticalScale*(yAxisLabelsGap+1)),
			NewStyle(ColorWhite),
			image.Pt(self.Inner.Min.X, self.Inner.Max.Y-(i*(yAxisLabelsGap+1))-2),
		)
	}
}

func (self *Plot) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	maxVal := self.MaxVal
	if maxVal == 0 {
		maxVal, _ = GetMaxFloat64From2dSlice(self.Data)
	}

	if self.ShowAxes {
		self.plotAxes(buf, maxVal)
	}

	drawArea := self.Inner
	if self.ShowAxes {
		drawArea = image.Rect(
			self.Inner.Min.X+yAxisLabelsWidth+1, self.Inner.Min.Y,
			self.Inner.Max.X, self.Inner.Max.Y-xAxisLabelsHeight-1,
		)
	}

	switch self.Marker {
	case MarkerBraille:
		self.renderBraille(buf, drawArea, maxVal)
	case MarkerDot:
		self.renderDot(buf, drawArea, maxVal)
	}
}


================================================
FILE: widgets/sparkline.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	. "github.com/gizak/termui/v3"
)

// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers.
type Sparkline struct {
	Data       []float64
	Title      string
	TitleStyle Style
	LineColor  Color
	MaxVal     float64
	MaxHeight  int
}

// SparklineGroup is a renderable widget which groups together the given sparklines.
type SparklineGroup struct {
	Block
	Sparklines []*Sparkline
}

// NewSparkline returns a unrenderable single sparkline that needs to be added to a SparklineGroup
func NewSparkline() *Sparkline {
	return &Sparkline{
		TitleStyle: Theme.Sparkline.Title,
		LineColor:  Theme.Sparkline.Line,
	}
}

func NewSparklineGroup(sls ...*Sparkline) *SparklineGroup {
	return &SparklineGroup{
		Block:      *NewBlock(),
		Sparklines: sls,
	}
}

func (self *SparklineGroup) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	sparklineHeight := self.Inner.Dy() / len(self.Sparklines)

	for i, sl := range self.Sparklines {
		heightOffset := (sparklineHeight * (i + 1))
		barHeight := sparklineHeight
		if i == len(self.Sparklines)-1 {
			heightOffset = self.Inner.Dy()
			barHeight = self.Inner.Dy() - (sparklineHeight * i)
		}
		if sl.Title != "" {
			barHeight--
		}

		maxVal := sl.MaxVal
		if maxVal == 0 {
			maxVal, _ = GetMaxFloat64FromSlice(sl.Data)
		}

		// draw line
		for j := 0; j < len(sl.Data) && j < self.Inner.Dx(); j++ {
			data := sl.Data[j]
			height := int((data / maxVal) * float64(barHeight))
			if height > sl.MaxHeight {
				height = sl.MaxHeight
			}
			sparkChar := BARS[len(BARS)-1]
			for k := 0; k < height; k++ {
				buf.SetCell(
					NewCell(sparkChar, NewStyle(sl.LineColor)),
					image.Pt(j+self.Inner.Min.X, self.Inner.Min.Y-1+heightOffset-k),
				)
			}
			if height == 0 {
				sparkChar = BARS[1]
				buf.SetCell(
					NewCell(sparkChar, NewStyle(sl.LineColor)),
					image.Pt(j+self.Inner.Min.X, self.Inner.Min.Y-1+heightOffset),
				)
			}
		}

		if sl.Title != "" {
			// draw title
			buf.SetString(
				TrimString(sl.Title, self.Inner.Dx()),
				sl.TitleStyle,
				image.Pt(self.Inner.Min.X, self.Inner.Min.Y-1+heightOffset-barHeight),
			)
		}
	}
}


================================================
FILE: widgets/stacked_barchart.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"fmt"
	"image"

	rw "github.com/mattn/go-runewidth"

	. "github.com/gizak/termui/v3"
)

type StackedBarChart struct {
	Block
	BarColors    []Color
	LabelStyles  []Style
	NumStyles    []Style // only Fg and Modifier are used
	NumFormatter func(float64) string
	Data         [][]float64
	Labels       []string
	BarWidth     int
	BarGap       int
	MaxVal       float64
}

func NewStackedBarChart() *StackedBarChart {
	return &StackedBarChart{
		Block:        *NewBlock(),
		BarColors:    Theme.StackedBarChart.Bars,
		LabelStyles:  Theme.StackedBarChart.Labels,
		NumStyles:    Theme.StackedBarChart.Nums,
		NumFormatter: func(n float64) string { return fmt.Sprint(n) },
		BarGap:       1,
		BarWidth:     3,
	}
}

func (self *StackedBarChart) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	maxVal := self.MaxVal
	if maxVal == 0 {
		for _, data := range self.Data {
			maxVal = MaxFloat64(maxVal, SumFloat64Slice(data))
		}
	}

	barXCoordinate := self.Inner.Min.X

	for i, bar := range self.Data {
		// draw stacked bars
		stackedBarYCoordinate := 0
		for j, data := range bar {
			// draw each stacked bar
			height := int((data / maxVal) * float64(self.Inner.Dy()-1))
			for x := barXCoordinate; x < MinInt(barXCoordinate+self.BarWidth, self.Inner.Max.X); x++ {
				for y := (self.Inner.Max.Y - 2) - stackedBarYCoordinate; y > (self.Inner.Max.Y-2)-stackedBarYCoordinate-height; y-- {
					c := NewCell(' ', NewStyle(ColorClear, SelectColor(self.BarColors, j)))
					buf.SetCell(c, image.Pt(x, y))
				}
			}

			// draw number
			numberXCoordinate := barXCoordinate + int((float64(self.BarWidth) / 2)) - 1
			buf.SetString(
				self.NumFormatter(data),
				NewStyle(
					SelectStyle(self.NumStyles, j+1).Fg,
					SelectColor(self.BarColors, j),
					SelectStyle(self.NumStyles, j+1).Modifier,
				),
				image.Pt(numberXCoordinate, (self.Inner.Max.Y-2)-stackedBarYCoordinate),
			)

			stackedBarYCoordinate += height
		}

		// draw label
		if i < len(self.Labels) {
			labelXCoordinate := barXCoordinate + MaxInt(
				int((float64(self.BarWidth)/2))-int((float64(rw.StringWidth(self.Labels[i]))/2)),
				0,
			)
			buf.SetString(
				TrimString(self.Labels[i], self.BarWidth),
				SelectStyle(self.LabelStyles, i),
				image.Pt(labelXCoordinate, self.Inner.Max.Y-1),
			)
		}

		barXCoordinate += (self.BarWidth + self.BarGap)
	}
}


================================================
FILE: widgets/table.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	. "github.com/gizak/termui/v3"
)

/*Table is like:
┌ Awesome Table ───────────────────────────────────────────────┐
│  Col0          | Col1 | Col2 | Col3  | Col4  | Col5  | Col6  |
│──────────────────────────────────────────────────────────────│
│  Some Item #1  | AAA  | 123  | CCCCC | EEEEE | GGGGG | IIIII |
│──────────────────────────────────────────────────────────────│
│  Some Item #2  | BBB  | 456  | DDDDD | FFFFF | HHHHH | JJJJJ |
└──────────────────────────────────────────────────────────────┘
*/
type Table struct {
	Block
	Rows          [][]string
	ColumnWidths  []int
	TextStyle     Style
	RowSeparator  bool
	TextAlignment Alignment
	RowStyles     map[int]Style
	FillRow       bool

	// ColumnResizer is called on each Draw. Can be used for custom column sizing.
	ColumnResizer func()
}

func NewTable() *Table {
	return &Table{
		Block:         *NewBlock(),
		TextStyle:     Theme.Table.Text,
		RowSeparator:  true,
		RowStyles:     make(map[int]Style),
		ColumnResizer: func() {},
	}
}

func (self *Table) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	self.ColumnResizer()

	columnWidths := self.ColumnWidths
	if len(columnWidths) == 0 {
		columnCount := len(self.Rows[0])
		columnWidth := self.Inner.Dx() / columnCount
		for i := 0; i < columnCount; i++ {
			columnWidths = append(columnWidths, columnWidth)
		}
	}

	yCoordinate := self.Inner.Min.Y

	// draw rows
	for i := 0; i < len(self.Rows) && yCoordinate < self.Inner.Max.Y; i++ {
		row := self.Rows[i]
		colXCoordinate := self.Inner.Min.X

		rowStyle := self.TextStyle
		// get the row style if one exists
		if style, ok := self.RowStyles[i]; ok {
			rowStyle = style
		}

		if self.FillRow {
			blankCell := NewCell(' ', rowStyle)
			buf.Fill(blankCell, image.Rect(self.Inner.Min.X, yCoordinate, self.Inner.Max.X, yCoordinate+1))
		}

		// draw row cells
		for j := 0; j < len(row); j++ {
			col := ParseStyles(row[j], rowStyle)
			// draw row cell
			if len(col) > columnWidths[j] || self.TextAlignment == AlignLeft {
				for _, cx := range BuildCellWithXArray(col) {
					k, cell := cx.X, cx.Cell
					if k == columnWidths[j] || colXCoordinate+k == self.Inner.Max.X {
						cell.Rune = ELLIPSES
						buf.SetCell(cell, image.Pt(colXCoordinate+k-1, yCoordinate))
						break
					} else {
						buf.SetCell(cell, image.Pt(colXCoordinate+k, yCoordinate))
					}
				}
			} else if self.TextAlignment == AlignCenter {
				xCoordinateOffset := (columnWidths[j] - len(col)) / 2
				stringXCoordinate := xCoordinateOffset + colXCoordinate
				for _, cx := range BuildCellWithXArray(col) {
					k, cell := cx.X, cx.Cell
					buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate))
				}
			} else if self.TextAlignment == AlignRight {
				stringXCoordinate := MinInt(colXCoordinate+columnWidths[j], self.Inner.Max.X) - len(col)
				for _, cx := range BuildCellWithXArray(col) {
					k, cell := cx.X, cx.Cell
					buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate))
				}
			}
			colXCoordinate += columnWidths[j] + 1
		}

		// draw vertical separators
		separatorStyle := self.Block.BorderStyle

		separatorXCoordinate := self.Inner.Min.X
		verticalCell := NewCell(VERTICAL_LINE, separatorStyle)
		for i, width := range columnWidths {
			if self.FillRow && i < len(columnWidths)-1 {
				verticalCell.Style.Bg = rowStyle.Bg
			} else {
				verticalCell.Style.Bg = self.Block.BorderStyle.Bg
			}

			separatorXCoordinate += width
			buf.SetCell(verticalCell, image.Pt(separatorXCoordinate, yCoordinate))
			separatorXCoordinate++
		}

		yCoordinate++

		// draw horizontal separator
		horizontalCell := NewCell(HORIZONTAL_LINE, separatorStyle)
		if self.RowSeparator && yCoordinate < self.Inner.Max.Y && i != len(self.Rows)-1 {
			buf.Fill(horizontalCell, image.Rect(self.Inner.Min.X, yCoordinate, self.Inner.Max.X, yCoordinate+1))
			yCoordinate++
		}
	}
}


================================================
FILE: widgets/tabs.go
================================================
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	. "github.com/gizak/termui/v3"
)

// TabPane is a renderable widget which can be used to conditionally render certain tabs/views.
// TabPane shows a list of Tab names.
// The currently selected tab can be found through the `ActiveTabIndex` field.
type TabPane struct {
	Block
	TabNames         []string
	ActiveTabIndex   int
	ActiveTabStyle   Style
	InactiveTabStyle Style
}

func NewTabPane(names ...string) *TabPane {
	return &TabPane{
		Block:            *NewBlock(),
		TabNames:         names,
		ActiveTabStyle:   Theme.Tab.Active,
		InactiveTabStyle: Theme.Tab.Inactive,
	}
}

func (self *TabPane) FocusLeft() {
	if self.ActiveTabIndex > 0 {
		self.ActiveTabIndex--
	}
}

func (self *TabPane) FocusRight() {
	if self.ActiveTabIndex < len(self.TabNames)-1 {
		self.ActiveTabIndex++
	}
}

func (self *TabPane) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	xCoordinate := self.Inner.Min.X
	for i, name := range self.TabNames {
		ColorPair := self.InactiveTabStyle
		if i == self.ActiveTabIndex {
			ColorPair = self.ActiveTabStyle
		}
		buf.SetString(
			TrimString(name, self.Inner.Max.X-xCoordinate),
			ColorPair,
			image.Pt(xCoordinate, self.Inner.Min.Y),
		)

		xCoordinate += 1 + len(name)

		if i < len(self.TabNames)-1 && xCoordinate < self.Inner.Max.X {
			buf.SetCell(
				NewCell(VERTICAL_LINE, NewStyle(ColorWhite)),
				image.Pt(xCoordinate, self.Inner.Min.Y),
			)
		}

		xCoordinate += 2
	}
}


================================================
FILE: widgets/tree.go
================================================
package widgets

import (
	"fmt"
	"image"
	"strings"

	. "github.com/gizak/termui/v3"
	rw "github.com/mattn/go-runewidth"
)

const treeIndent = "  "

// TreeNode is a tree node.
type TreeNode struct {
	Value    fmt.Stringer
	Expanded bool
	Nodes    []*TreeNode

	// level stores the node level in the tree.
	level int
}

// TreeWalkFn is a function used for walking a Tree.
// To interrupt the walking process function should return false.
type TreeWalkFn func(*TreeNode) bool

func (self *TreeNode) parseStyles(style Style) []Cell {
	var sb strings.Builder
	if len(self.Nodes) == 0 {
		sb.WriteString(strings.Repeat(treeIndent, self.level+1))
	} else {
		sb.WriteString(strings.Repeat(treeIndent, self.level))
		if self.Expanded {
			sb.WriteRune(Theme.Tree.Expanded)
		} else {
			sb.WriteRune(Theme.Tree.Collapsed)
		}
		sb.WriteByte(' ')
	}
	sb.WriteString(self.Value.String())
	return ParseStyles(sb.String(), style)
}

// Tree is a tree widget.
type Tree struct {
	Block
	TextStyle        Style
	SelectedRowStyle Style
	WrapText         bool
	SelectedRow      int

	nodes []*TreeNode
	// rows is flatten nodes for rendering.
	rows   []*TreeNode
	topRow int
}

// NewTree creates a new Tree widget.
func NewTree() *Tree {
	return &Tree{
		Block:            *NewBlock(),
		TextStyle:        Theme.Tree.Text,
		SelectedRowStyle: Theme.Tree.Text,
		WrapText:         true,
	}
}

func (self *Tree) SetNodes(nodes []*TreeNode) {
	self.nodes = nodes
	self.prepareNodes()
}

func (self *Tree) prepareNodes() {
	self.rows = make([]*TreeNode, 0)
	for _, node := range self.nodes {
		self.prepareNode(node, 0)
	}
}

func (self *Tree) prepareNode(node *TreeNode, level int) {
	self.rows = append(self.rows, node)
	node.level = level

	if node.Expanded {
		for _, n := range node.Nodes {
			self.prepareNode(n, level+1)
		}
	}
}

func (self *Tree) Walk(fn TreeWalkFn) {
	for _, n := range self.nodes {
		if !self.walk(n, fn) {
			break
		}
	}
}

func (self *Tree) walk(n *TreeNode, fn TreeWalkFn) bool {
	if !fn(n) {
		return false
	}

	for _, node := range n.Nodes {
		if !self.walk(node, fn) {
			return false
		}
	}

	return true
}

func (self *Tree) Draw(buf *Buffer) {
	self.Block.Draw(buf)
	point := self.Inner.Min

	// adjusts view into widget
	if self.SelectedRow >= self.Inner.Dy()+self.topRow {
		self.topRow = self.SelectedRow - self.Inner.Dy() + 1
	} else if self.SelectedRow < self.topRow {
		self.topRow = self.SelectedRow
	}

	// draw rows
	for row := self.topRow; row < len(self.rows) && point.Y < self.Inner.Max.Y; row++ {
		cells := self.rows[row].parseStyles(self.TextStyle)
		if self.WrapText {
			cells = WrapCells(cells, uint(self.Inner.Dx()))
		}
		for j := 0; j < len(cells) && point.Y < self.Inner.Max.Y; j++ {
			style := cells[j].Style
			if row == self.SelectedRow {
				style = self.SelectedRowStyle
			}
			if point.X+1 == self.Inner.Max.X+1 && len(cells) > self.Inner.Dx() {
				buf.SetCell(NewCell(ELLIPSES, style), point.Add(image.Pt(-1, 0)))
			} else {
				buf.SetCell(NewCell(cells[j].Rune, style), point)
				point = point.Add(image.Pt(rw.RuneWidth(cells[j].Rune), 0))
			}
		}
		point = image.Pt(self.Inner.Min.X, point.Y+1)
	}

	// draw UP_ARROW if needed
	if self.topRow > 0 {
		buf.SetCell(
			NewCell(UP_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Min.Y),
		)
	}

	// draw DOWN_ARROW if needed
	if len(self.rows) > int(self.topRow)+self.Inner.Dy() {
		buf.SetCell(
			NewCell(DOWN_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Max.Y-1),
		)
	}
}

// ScrollAmount scrolls by amount given. If amount is < 0, then scroll up.
// There is no need to set self.topRow, as this will be set automatically when drawn,
// since if the selected item is off screen then the topRow variable will change accordingly.
func (self *Tree) ScrollAmount(amount int) {
	if len(self.rows)-int(self.SelectedRow) <= amount {
		self.SelectedRow = len(self.rows) - 1
	} else if int(self.SelectedRow)+amount < 0 {
		self.SelectedRow = 0
	} else {
		self.SelectedRow += amount
	}
}

func (self *Tree) SelectedNode() *TreeNode {
	if len(self.rows) == 0 {
		return nil
	}
	return self.rows[self.SelectedRow]
}

func (self *Tree) ScrollUp() {
	self.ScrollAmount(-1)
}

func (self *Tree) ScrollDown() {
	self.ScrollAmount(1)
}

func (self *Tree) ScrollPageUp() {
	// If an item is selected below top row, then go to the top row.
	if self.SelectedRow > self.topRow {
		self.SelectedRow = self.topRow
	} else {
		self.ScrollAmount(-self.Inner.Dy())
	}
}

func (self *Tree) ScrollPageDown() {
	self.ScrollAmount(self.Inner.Dy())
}

func (self *Tree) ScrollHalfPageUp() {
	self.ScrollAmount(-int(FloorFloat64(float64(self.Inner.Dy()) / 2)))
}

func (self *Tree) ScrollHalfPageDown() {
	self.ScrollAmount(int(FloorFloat64(float64(self.Inner.Dy()) / 2)))
}

func (self *Tree) ScrollTop() {
	self.SelectedRow = 0
}

func (self *Tree) ScrollBottom() {
	self.SelectedRow = len(self.rows) - 1
}

func (self *Tree) Collapse() {
	self.rows[self.SelectedRow].Expanded = false
	self.prepareNodes()
}

func (self *Tree) Expand() {
	node := self.rows[self.SelectedRow]
	if len(node.Nodes) > 0 {
		self.rows[self.SelectedRow].Expanded = true
	}
	self.prepareNodes()
}

func (self *Tree) ToggleExpand() {
	node := self.rows[self.SelectedRow]
	if len(node.Nodes) > 0 {
		node.Expanded = !node.Expanded
	}
	self.prepareNodes()
}

func (self *Tree) ExpandAll() {
	self.Walk(func(n *TreeNode) bool {
		if len(n.Nodes) > 0 {
			n.Expanded = true
		}
		return true
	})
	self.prepareNodes()
}

func (self *Tree) CollapseAll() {
	self.Walk(func(n *TreeNode) bool {
		n.Expanded = false
		return true
	})
	self.prepareNodes()
}
Download .txt
gitextract_vw2ezebp/

├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── _examples/
│   ├── barchart.go
│   ├── canvas.go
│   ├── demo.go
│   ├── gauge.go
│   ├── grid.go
│   ├── hello_world.go
│   ├── image.go
│   ├── list.go
│   ├── paragraph.go
│   ├── piechart.go
│   ├── plot.go
│   ├── sparkline.go
│   ├── stacked_barchart.go
│   ├── table.go
│   ├── tabs.go
│   └── tree.go
├── _scripts/
│   └── copyright_header.py
├── _test/
│   └── log_events.go
├── alignment.go
├── backend.go
├── block.go
├── buffer.go
├── canvas.go
├── doc.go
├── drawille/
│   └── drawille.go
├── events.go
├── go.mod
├── go.sum
├── grid.go
├── render.go
├── style.go
├── style_parser.go
├── symbols.go
├── symbols_other.go
├── symbols_windows.go
├── theme.go
├── utils.go
└── widgets/
    ├── barchart.go
    ├── gauge.go
    ├── image.go
    ├── list.go
    ├── paragraph.go
    ├── piechart.go
    ├── plot.go
    ├── sparkline.go
    ├── stacked_barchart.go
    ├── table.go
    ├── tabs.go
    └── tree.go
Download .txt
SYMBOL INDEX (296 symbols across 46 files)

FILE: _examples/barchart.go
  function main (line 16) | func main() {

FILE: _examples/canvas.go
  function main (line 12) | func main() {

FILE: _examples/demo.go
  function main (line 18) | func main() {

FILE: _examples/gauge.go
  function main (line 17) | func main() {

FILE: _examples/grid.go
  function main (line 18) | func main() {

FILE: _examples/hello_world.go
  function main (line 12) | func main() {

FILE: _examples/image.go
  function main (line 25) | func main() {
  constant GOPHER_IMAGE (line 90) | GOPHER_IMAGE = `iVBORw0KGgoAAAANSUhEUgAAAEsAAAA8CAAAAAALAhhPAAAFfUlEQVRY...

FILE: _examples/list.go
  function main (line 16) | func main() {

FILE: _examples/paragraph.go
  function main (line 16) | func main() {

FILE: _examples/piechart.go
  function main (line 18) | func main() {

FILE: _examples/plot.go
  function main (line 17) | func main() {

FILE: _examples/sparkline.go
  function main (line 16) | func main() {

FILE: _examples/stacked_barchart.go
  function main (line 16) | func main() {

FILE: _examples/table.go
  function main (line 16) | func main() {

FILE: _examples/tabs.go
  function main (line 16) | func main() {

FILE: _examples/tree.go
  type nodeValue (line 12) | type nodeValue
    method String (line 14) | func (nv nodeValue) String() string {
  function main (line 18) | func main() {

FILE: _scripts/copyright_header.py
  function is_target (line 18) | def is_target(fpath):
  function update_copyright (line 24) | def update_copyright(fpath):
  function main (line 42) | def main():

FILE: _test/log_events.go
  function main (line 18) | func main() {

FILE: alignment.go
  type Alignment (line 3) | type Alignment
  constant AlignLeft (line 6) | AlignLeft Alignment = iota
  constant AlignCenter (line 7) | AlignCenter
  constant AlignRight (line 8) | AlignRight

FILE: backend.go
  function Init (line 13) | func Init() error {
  function Close (line 23) | func Close() {
  function TerminalDimensions (line 27) | func TerminalDimensions() (int, int) {
  function Clear (line 33) | func Clear() {

FILE: block.go
  type Block (line 16) | type Block struct
    method drawBorder (line 46) | func (self *Block) drawBorder(buf *Buffer) {
    method Draw (line 80) | func (self *Block) Draw(buf *Buffer) {
    method SetRect (line 92) | func (self *Block) SetRect(x1, y1, x2, y2 int) {
    method GetRect (line 103) | func (self *Block) GetRect() image.Rectangle {
  function NewBlock (line 33) | func NewBlock() *Block {

FILE: buffer.go
  type Cell (line 14) | type Cell struct
  function NewCell (line 27) | func NewCell(rune rune, args ...interface{}) Cell {
  type Buffer (line 39) | type Buffer struct
    method GetCell (line 53) | func (self *Buffer) GetCell(p image.Point) Cell {
    method SetCell (line 57) | func (self *Buffer) SetCell(c Cell, p image.Point) {
    method Fill (line 61) | func (self *Buffer) Fill(c Cell, rect image.Rectangle) {
    method SetString (line 69) | func (self *Buffer) SetString(s string, style Style, p image.Point) {
  function NewBuffer (line 44) | func NewBuffer(r image.Rectangle) *Buffer {

FILE: canvas.go
  type Canvas (line 9) | type Canvas struct
    method SetPoint (line 21) | func (self *Canvas) SetPoint(p image.Point, color Color) {
    method SetLine (line 25) | func (self *Canvas) SetLine(p0, p1 image.Point, color Color) {
    method Draw (line 29) | func (self *Canvas) Draw(buf *Buffer) {
  function NewCanvas (line 14) | func NewCanvas() *Canvas {

FILE: drawille/drawille.go
  constant BRAILLE_OFFSET (line 7) | BRAILLE_OFFSET = '\u2800'
  type Color (line 16) | type Color
  type Cell (line 18) | type Cell struct
  type Canvas (line 23) | type Canvas struct
    method SetPoint (line 33) | func (self *Canvas) SetPoint(p image.Point, color Color) {
    method SetLine (line 41) | func (self *Canvas) SetLine(p0, p1 image.Point, color Color) {
    method GetCells (line 47) | func (self *Canvas) GetCells() map[image.Point]Cell {
  function NewCanvas (line 27) | func NewCanvas() *Canvas {
  function line (line 55) | func line(p0, p1 image.Point) []image.Point {
  function absInt (line 85) | func absInt(x int) int {

FILE: events.go
  type EventType (line 42) | type EventType
  constant KeyboardEvent (line 45) | KeyboardEvent EventType = iota
  constant MouseEvent (line 46) | MouseEvent
  constant ResizeEvent (line 47) | ResizeEvent
  type Event (line 50) | type Event struct
  type Mouse (line 57) | type Mouse struct
  type Resize (line 64) | type Resize struct
  function PollEvents (line 70) | func PollEvents() <-chan Event {
  function convertTermboxKeyboardEvent (line 142) | func convertTermboxKeyboardEvent(e tb.Event) Event {
  function convertTermboxMouseEvent (line 173) | func convertTermboxMouseEvent(e tb.Event) Event {
  function convertTermboxEvent (line 191) | func convertTermboxEvent(e tb.Event) Event {

FILE: grid.go
  type gridItemType (line 7) | type gridItemType
  constant col (line 10) | col gridItemType = 0
  constant row (line 11) | row gridItemType = 1
  type Grid (line 14) | type Grid struct
    method Set (line 72) | func (self *Grid) Set(entries ...interface{}) {
    method setHelper (line 82) | func (self *Grid) setHelper(item GridItem, parentWidthRatio, parentHei...
    method Draw (line 135) | func (self *Grid) Draw(buf *Buffer) {
  type GridItem (line 21) | type GridItem struct
  function NewGrid (line 32) | func NewGrid() *Grid {
  function NewCol (line 41) | func NewCol(ratio float64, i ...interface{}) GridItem {
  function NewRow (line 56) | func NewRow(ratio float64, i ...interface{}) GridItem {

FILE: render.go
  type Drawable (line 14) | type Drawable interface
  function Render (line 21) | func Render(items ...Drawable) {

FILE: style.go
  type Color (line 6) | type Color
  constant ColorClear (line 9) | ColorClear Color = -1
  constant ColorBlack (line 13) | ColorBlack   Color = 0
  constant ColorRed (line 14) | ColorRed     Color = 1
  constant ColorGreen (line 15) | ColorGreen   Color = 2
  constant ColorYellow (line 16) | ColorYellow  Color = 3
  constant ColorBlue (line 17) | ColorBlue    Color = 4
  constant ColorMagenta (line 18) | ColorMagenta Color = 5
  constant ColorCyan (line 19) | ColorCyan    Color = 6
  constant ColorWhite (line 20) | ColorWhite   Color = 7
  type Modifier (line 23) | type Modifier
  constant ModifierClear (line 27) | ModifierClear     Modifier = 0
  constant ModifierBold (line 28) | ModifierBold      Modifier = 1 << 9
  constant ModifierUnderline (line 29) | ModifierUnderline Modifier = 1 << 10
  constant ModifierReverse (line 30) | ModifierReverse   Modifier = 1 << 11
  type Style (line 34) | type Style struct
  function NewStyle (line 51) | func NewStyle(fg Color, args ...interface{}) Style {

FILE: style_parser.go
  constant tokenFg (line 12) | tokenFg       = "fg"
  constant tokenBg (line 13) | tokenBg       = "bg"
  constant tokenModifier (line 14) | tokenModifier = "mod"
  constant tokenItemSeparator (line 16) | tokenItemSeparator  = ","
  constant tokenValueSeparator (line 17) | tokenValueSeparator = ":"
  constant tokenBeginStyledText (line 19) | tokenBeginStyledText = '['
  constant tokenEndStyledText (line 20) | tokenEndStyledText   = ']'
  constant tokenBeginStyle (line 22) | tokenBeginStyle = '('
  constant tokenEndStyle (line 23) | tokenEndStyle   = ')'
  type parserState (line 26) | type parserState
  constant parserStateDefault (line 29) | parserStateDefault parserState = iota
  constant parserStateStyleItems (line 30) | parserStateStyleItems
  constant parserStateStyledText (line 31) | parserStateStyledText
  function readStyle (line 54) | func readStyle(runes []rune, defaultStyle Style) Style {
  function ParseStyles (line 77) | func ParseStyles(s string, defaultStyle Style) []Cell {

FILE: symbols.go
  constant DOT (line 4) | DOT      = '•'
  constant ELLIPSES (line 5) | ELLIPSES = '…'
  constant UP_ARROW (line 7) | UP_ARROW   = '▲'
  constant DOWN_ARROW (line 8) | DOWN_ARROW = '▼'
  constant COLLAPSED (line 10) | COLLAPSED = '+'
  constant EXPANDED (line 11) | EXPANDED  = '−'

FILE: symbols_other.go
  constant TOP_LEFT (line 10) | TOP_LEFT     = '┌'
  constant TOP_RIGHT (line 11) | TOP_RIGHT    = '┐'
  constant BOTTOM_LEFT (line 12) | BOTTOM_LEFT  = '└'
  constant BOTTOM_RIGHT (line 13) | BOTTOM_RIGHT = '┘'
  constant VERTICAL_LINE (line 15) | VERTICAL_LINE   = '│'
  constant HORIZONTAL_LINE (line 16) | HORIZONTAL_LINE = '─'
  constant VERTICAL_LEFT (line 18) | VERTICAL_LEFT   = '┤'
  constant VERTICAL_RIGHT (line 19) | VERTICAL_RIGHT  = '├'
  constant HORIZONTAL_UP (line 20) | HORIZONTAL_UP   = '┴'
  constant HORIZONTAL_DOWN (line 21) | HORIZONTAL_DOWN = '┬'
  constant QUOTA_LEFT (line 23) | QUOTA_LEFT  = '«'
  constant QUOTA_RIGHT (line 24) | QUOTA_RIGHT = '»'
  constant VERTICAL_DASH (line 26) | VERTICAL_DASH   = '┊'
  constant HORIZONTAL_DASH (line 27) | HORIZONTAL_DASH = '┈'

FILE: symbols_windows.go
  constant TOP_LEFT (line 10) | TOP_LEFT     = '+'
  constant TOP_RIGHT (line 11) | TOP_RIGHT    = '+'
  constant BOTTOM_LEFT (line 12) | BOTTOM_LEFT  = '+'
  constant BOTTOM_RIGHT (line 13) | BOTTOM_RIGHT = '+'
  constant VERTICAL_LINE (line 15) | VERTICAL_LINE   = '|'
  constant HORIZONTAL_LINE (line 16) | HORIZONTAL_LINE = '-'
  constant VERTICAL_LEFT (line 18) | VERTICAL_LEFT   = '+'
  constant VERTICAL_RIGHT (line 19) | VERTICAL_RIGHT  = '+'
  constant HORIZONTAL_UP (line 20) | HORIZONTAL_UP   = '+'
  constant HORIZONTAL_DOWN (line 21) | HORIZONTAL_DOWN = '+'
  constant QUOTA_LEFT (line 23) | QUOTA_LEFT  = '<'
  constant QUOTA_RIGHT (line 24) | QUOTA_RIGHT = '>'
  constant VERTICAL_DASH (line 26) | VERTICAL_DASH   = '|'
  constant HORIZONTAL_DASH (line 27) | HORIZONTAL_DASH = '-'

FILE: theme.go
  type RootTheme (line 27) | type RootTheme struct
  type BlockTheme (line 45) | type BlockTheme struct
  type BarChartTheme (line 50) | type BarChartTheme struct
  type GaugeTheme (line 56) | type GaugeTheme struct
  type PlotTheme (line 61) | type PlotTheme struct
  type ListTheme (line 66) | type ListTheme struct
  type TreeTheme (line 70) | type TreeTheme struct
  type ParagraphTheme (line 76) | type ParagraphTheme struct
  type PieChartTheme (line 80) | type PieChartTheme struct
  type SparklineTheme (line 84) | type SparklineTheme struct
  type StackedBarChartTheme (line 89) | type StackedBarChartTheme struct
  type TabTheme (line 95) | type TabTheme struct
  type TableTheme (line 100) | type TableTheme struct

FILE: utils.go
  function InterfaceSlice (line 18) | func InterfaceSlice(slice interface{}) []interface{} {
  function TrimString (line 34) | func TrimString(s string, w int) string {
  function SelectColor (line 44) | func SelectColor(colors []Color, index int) Color {
  function SelectStyle (line 48) | func SelectStyle(styles []Style, index int) Style {
  function SumIntSlice (line 54) | func SumIntSlice(slice []int) int {
  function SumFloat64Slice (line 62) | func SumFloat64Slice(data []float64) float64 {
  function GetMaxIntFromSlice (line 70) | func GetMaxIntFromSlice(slice []int) (int, error) {
  function GetMaxFloat64FromSlice (line 83) | func GetMaxFloat64FromSlice(slice []float64) (float64, error) {
  function GetMaxFloat64From2dSlice (line 96) | func GetMaxFloat64From2dSlice(slices [][]float64) (float64, error) {
  function RoundFloat64 (line 111) | func RoundFloat64(x float64) float64 {
  function FloorFloat64 (line 115) | func FloorFloat64(x float64) float64 {
  function AbsInt (line 119) | func AbsInt(x int) int {
  function MinFloat64 (line 126) | func MinFloat64(x, y float64) float64 {
  function MaxFloat64 (line 133) | func MaxFloat64(x, y float64) float64 {
  function MaxInt (line 140) | func MaxInt(x, y int) int {
  function MinInt (line 147) | func MinInt(x, y int) int {
  function WrapCells (line 157) | func WrapCells(cells []Cell, width uint) []Cell {
  function RunesToStyledCells (line 173) | func RunesToStyledCells(runes []rune, style Style) []Cell {
  function CellsToString (line 181) | func CellsToString(cells []Cell) string {
  function TrimCells (line 189) | func TrimCells(cells []Cell, w int) []Cell {
  function SplitCells (line 200) | func SplitCells(cells []Cell, r rune) [][]Cell {
  type CellWithX (line 217) | type CellWithX struct
  function BuildCellWithXArray (line 222) | func BuildCellWithXArray(cells []Cell) []CellWithX {

FILE: widgets/barchart.go
  type BarChart (line 16) | type BarChart struct
    method Draw (line 41) | func (self *BarChart) Draw(buf *Buffer) {
  function NewBarChart (line 29) | func NewBarChart() *BarChart {

FILE: widgets/gauge.go
  type Gauge (line 14) | type Gauge struct
    method Draw (line 30) | func (self *Gauge) Draw(buf *Buffer) {
  function NewGauge (line 22) | func NewGauge() *Gauge {

FILE: widgets/image.go
  type Image (line 14) | type Image struct
    method Draw (line 30) | func (self *Image) Draw(buf *Buffer) {
    method colorAverage (line 105) | func (self *Image) colorAverage(x0, x1, y0, y1 int) colorAverager {
  function NewImage (line 22) | func NewImage(img image.Image) *Image {
  type colorAverager (line 120) | type colorAverager struct
    method add (line 124) | func (self colorAverager) add(col color.Color) colorAverager {
    method RGBA (line 135) | func (self colorAverager) RGBA() (uint32, uint32, uint32, uint32) {
    method fgColor (line 145) | func (self colorAverager) fgColor() Color {
    method ch (line 149) | func (self colorAverager) ch() rune {
    method monochrome (line 165) | func (self colorAverager) monochrome(threshold uint8, invert bool) bool {
  type paletteColor (line 169) | type paletteColor struct
    method RGBA (line 174) | func (self paletteColor) RGBA() (uint32, uint32, uint32, uint32) {
  function blocksChar (line 189) | func blocksChar(ul, ur, ll, lr colorAverager, threshold uint8, invert bo...

FILE: widgets/list.go
  type List (line 15) | type List struct
    method Draw (line 33) | func (self *List) Draw(buf *Buffer) {
    method ScrollAmount (line 91) | func (self *List) ScrollAmount(amount int) {
    method ScrollUp (line 101) | func (self *List) ScrollUp() {
    method ScrollDown (line 105) | func (self *List) ScrollDown() {
    method ScrollPageUp (line 109) | func (self *List) ScrollPageUp() {
    method ScrollPageDown (line 118) | func (self *List) ScrollPageDown() {
    method ScrollHalfPageUp (line 122) | func (self *List) ScrollHalfPageUp() {
    method ScrollHalfPageDown (line 126) | func (self *List) ScrollHalfPageDown() {
    method ScrollTop (line 130) | func (self *List) ScrollTop() {
    method ScrollBottom (line 134) | func (self *List) ScrollBottom() {
  function NewList (line 25) | func NewList() *List {

FILE: widgets/paragraph.go
  type Paragraph (line 13) | type Paragraph struct
    method Draw (line 28) | func (self *Paragraph) Draw(buf *Buffer) {
  function NewParagraph (line 20) | func NewParagraph() *Paragraph {

FILE: widgets/piechart.go
  constant piechartOffsetUp (line 11) | piechartOffsetUp = -.5 * math.Pi
  constant resolutionFactor (line 12) | resolutionFactor = .0001
  constant fullCircle (line 13) | fullCircle       = 2.0 * math.Pi
  constant xStretch (line 14) | xStretch         = 2.0
  type PieChartLabel (line 18) | type PieChartLabel
  type PieChart (line 20) | type PieChart struct
    method Draw (line 37) | func (self *PieChart) Draw(buf *Buffer) {
  function NewPieChart (line 29) | func NewPieChart() *PieChart {
  type circle (line 82) | type circle struct
    method at (line 88) | func (self circle) at(phi float64) image.Point {
    method perimeter (line 95) | func (self circle) perimeter() float64 {
  type line (line 100) | type line struct
    method draw (line 105) | func (self line) draw(cell Cell, buf *Buffer) {
    method size (line 147) | func (self line) size() (w, h int) {

FILE: widgets/plot.go
  type Plot (line 18) | type Plot struct
    method renderBraille (line 79) | func (self *Plot) renderBraille(buf *Buffer, drawArea image.Rectangle,...
    method renderDot (line 121) | func (self *Plot) renderDot(buf *Buffer, drawArea image.Rectangle, max...
    method plotAxes (line 150) | func (self *Plot) plotAxes(buf *Buffer, maxVal float64) {
    method Draw (line 201) | func (self *Plot) Draw(buf *Buffer) {
  constant xAxisLabelsHeight (line 37) | xAxisLabelsHeight = 1
  constant yAxisLabelsWidth (line 38) | yAxisLabelsWidth  = 4
  constant xAxisLabelsGap (line 39) | xAxisLabelsGap    = 2
  constant yAxisLabelsGap (line 40) | yAxisLabelsGap    = 1
  type PlotType (line 43) | type PlotType
  constant LineChart (line 46) | LineChart PlotType = iota
  constant ScatterPlot (line 47) | ScatterPlot
  type PlotMarker (line 50) | type PlotMarker
  constant MarkerBraille (line 53) | MarkerBraille PlotMarker = iota
  constant MarkerDot (line 54) | MarkerDot
  type DrawDirection (line 57) | type DrawDirection
  constant DrawLeft (line 60) | DrawLeft DrawDirection = iota
  constant DrawRight (line 61) | DrawRight
  function NewPlot (line 64) | func NewPlot() *Plot {

FILE: widgets/sparkline.go
  type Sparkline (line 14) | type Sparkline struct
  type SparklineGroup (line 24) | type SparklineGroup struct
    method Draw (line 44) | func (self *SparklineGroup) Draw(buf *Buffer) {
  function NewSparkline (line 30) | func NewSparkline() *Sparkline {
  function NewSparklineGroup (line 37) | func NewSparklineGroup(sls ...*Sparkline) *SparklineGroup {

FILE: widgets/stacked_barchart.go
  type StackedBarChart (line 16) | type StackedBarChart struct
    method Draw (line 41) | func (self *StackedBarChart) Draw(buf *Buffer) {
  function NewStackedBarChart (line 29) | func NewStackedBarChart() *StackedBarChart {

FILE: widgets/table.go
  type Table (line 22) | type Table struct
    method Draw (line 46) | func (self *Table) Draw(buf *Buffer) {
  function NewTable (line 36) | func NewTable() *Table {

FILE: widgets/tabs.go
  type TabPane (line 16) | type TabPane struct
    method FocusLeft (line 33) | func (self *TabPane) FocusLeft() {
    method FocusRight (line 39) | func (self *TabPane) FocusRight() {
    method Draw (line 45) | func (self *TabPane) Draw(buf *Buffer) {
  function NewTabPane (line 24) | func NewTabPane(names ...string) *TabPane {

FILE: widgets/tree.go
  constant treeIndent (line 12) | treeIndent = "  "
  type TreeNode (line 15) | type TreeNode struct
    method parseStyles (line 28) | func (self *TreeNode) parseStyles(style Style) []Cell {
  type TreeWalkFn (line 26) | type TreeWalkFn
  type Tree (line 46) | type Tree struct
    method SetNodes (line 69) | func (self *Tree) SetNodes(nodes []*TreeNode) {
    method prepareNodes (line 74) | func (self *Tree) prepareNodes() {
    method prepareNode (line 81) | func (self *Tree) prepareNode(node *TreeNode, level int) {
    method Walk (line 92) | func (self *Tree) Walk(fn TreeWalkFn) {
    method walk (line 100) | func (self *Tree) walk(n *TreeNode, fn TreeWalkFn) bool {
    method Draw (line 114) | func (self *Tree) Draw(buf *Buffer) {
    method ScrollAmount (line 166) | func (self *Tree) ScrollAmount(amount int) {
    method SelectedNode (line 176) | func (self *Tree) SelectedNode() *TreeNode {
    method ScrollUp (line 183) | func (self *Tree) ScrollUp() {
    method ScrollDown (line 187) | func (self *Tree) ScrollDown() {
    method ScrollPageUp (line 191) | func (self *Tree) ScrollPageUp() {
    method ScrollPageDown (line 200) | func (self *Tree) ScrollPageDown() {
    method ScrollHalfPageUp (line 204) | func (self *Tree) ScrollHalfPageUp() {
    method ScrollHalfPageDown (line 208) | func (self *Tree) ScrollHalfPageDown() {
    method ScrollTop (line 212) | func (self *Tree) ScrollTop() {
    method ScrollBottom (line 216) | func (self *Tree) ScrollBottom() {
    method Collapse (line 220) | func (self *Tree) Collapse() {
    method Expand (line 225) | func (self *Tree) Expand() {
    method ToggleExpand (line 233) | func (self *Tree) ToggleExpand() {
    method ExpandAll (line 241) | func (self *Tree) ExpandAll() {
    method CollapseAll (line 251) | func (self *Tree) CollapseAll() {
  function NewTree (line 60) | func NewTree() *Tree {
Condensed preview — 54 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (123K chars).
[
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": ".DS_Store\n.vscode/\n.mypy_cache/\n.idea\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3081,
    "preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
  },
  {
    "path": "LICENSE",
    "chars": 1076,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Zack Guo\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "Makefile",
    "chars": 99,
    "preview": ".PHONY: run-examples\nrun-examples:\n\t@for file in _examples/*.go; do \\\n\t  go run $$file; \\\n\t  done;\n"
  },
  {
    "path": "README.md",
    "chars": 3562,
    "preview": "# termui\n\n[<img src=\"./_assets/demo.gif\" alt=\"demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)\" width=\"100%"
  },
  {
    "path": "_examples/barchart.go",
    "chars": 906,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/canvas.go",
    "chars": 426,
    "preview": "// +build ignore\n\npackage main\n\nimport (\n\t\"image\"\n\t\"log\"\n\n\tui \"github.com/gizak/termui/v3\"\n)\n\nfunc main() {\n\tif err := u"
  },
  {
    "path": "_examples/demo.go",
    "chars": 3872,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/gauge.go",
    "chars": 1582,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/grid.go",
    "chars": 2499,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/hello_world.go",
    "chars": 427,
    "preview": "// +build ignore\n\npackage main\n\nimport (\n\t\"log\"\n\n\tui \"github.com/gizak/termui/v3\"\n\t\"github.com/gizak/termui/v3/widgets\"\n"
  },
  {
    "path": "_examples/image.go",
    "chars": 4027,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/list.go",
    "chars": 1419,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/paragraph.go",
    "chars": 1809,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/piechart.go",
    "chars": 1285,
    "preview": "// +build ignore\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"math\"\n\t\"math/rand\"\n\t\"time\"\n\n\tui \"github.com/gizak/termui/v3\"\n\t\""
  },
  {
    "path": "_examples/plot.go",
    "chars": 1949,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/sparkline.go",
    "chars": 1454,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/stacked_barchart.go",
    "chars": 989,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/table.go",
    "chars": 1817,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/tabs.go",
    "chars": 1667,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "_examples/tree.go",
    "chars": 2019,
    "preview": "/// +build ignore\n\npackage main\n\nimport (\n\t\"log\"\n\n\tui \"github.com/gizak/termui/v3\"\n\t\"github.com/gizak/termui/v3/widgets\""
  },
  {
    "path": "_scripts/copyright_header.py",
    "chars": 1166,
    "preview": "#!/usr/bin/env python3\n\nimport re\nimport os\nimport io\n\ncopyright = \"\"\"// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>."
  },
  {
    "path": "_test/log_events.go",
    "chars": 654,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "alignment.go",
    "chars": 100,
    "preview": "package termui\n\ntype Alignment uint\n\nconst (\n\tAlignLeft Alignment = iota\n\tAlignCenter\n\tAlignRight\n)\n"
  },
  {
    "path": "backend.go",
    "chars": 770,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "block.go",
    "chars": 2726,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "buffer.go",
    "chars": 1553,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "canvas.go",
    "chars": 753,
    "preview": "package termui\n\nimport (\n\t\"image\"\n\n\t\"github.com/gizak/termui/v3/drawille\"\n)\n\ntype Canvas struct {\n\tBlock\n\tdrawille.Canva"
  },
  {
    "path": "doc.go",
    "chars": 280,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "drawille/drawille.go",
    "chars": 1821,
    "preview": "package drawille\n\nimport (\n\t\"image\"\n)\n\nconst BRAILLE_OFFSET = '\\u2800'\n\nvar BRAILLE = [4][2]rune{\n\t{'\\u0001', '\\u0008'},"
  },
  {
    "path": "events.go",
    "chars": 4819,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "go.mod",
    "chars": 213,
    "preview": "module github.com/gizak/termui/v3\n\ngo 1.15\n\nrequire (\n\tgithub.com/mattn/go-runewidth v0.0.15\n\tgithub.com/mitchellh/go-wo"
  },
  {
    "path": "go.sum",
    "chars": 1512,
    "preview": "github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=\ngithub.com/mattn/go-runewidth v0.0."
  },
  {
    "path": "grid.go",
    "chars": 3288,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "render.go",
    "chars": 768,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "style.go",
    "chars": 1297,
    "preview": "package termui\n\n// Color is an integer from -1 to 255\n// -1 = ColorClear\n// 0-255 = Xterm colors\ntype Color int\n\n// Colo"
  },
  {
    "path": "style_parser.go",
    "chars": 3860,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "symbols.go",
    "chars": 1024,
    "preview": "package termui\n\nconst (\n\tDOT      = '•'\n\tELLIPSES = '…'\n\n\tUP_ARROW   = '▲'\n\tDOWN_ARROW = '▼'\n\n\tCOLLAPSED = '+'\n\tEXPANDED"
  },
  {
    "path": "symbols_other.go",
    "chars": 523,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "symbols_windows.go",
    "chars": 522,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "theme.go",
    "chars": 2764,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "utils.go",
    "chars": 4580,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/barchart.go",
    "chars": 2244,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/gauge.go",
    "chars": 1385,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/image.go",
    "chars": 4960,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/list.go",
    "chars": 3405,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/paragraph.go",
    "chars": 958,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/piechart.go",
    "chars": 3827,
    "preview": "package widgets\n\nimport (\n\t\"image\"\n\t\"math\"\n\n\t. \"github.com/gizak/termui/v3\"\n)\n\nconst (\n\tpiechartOffsetUp = -.5 * math.Pi"
  },
  {
    "path": "widgets/plot.go",
    "chars": 5558,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/sparkline.go",
    "chars": 2325,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/stacked_barchart.go",
    "chars": 2528,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/table.go",
    "chars": 4050,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/tabs.go",
    "chars": 1617,
    "preview": "// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.\n// Use of this source code is governed by a MIT "
  },
  {
    "path": "widgets/tree.go",
    "chars": 5659,
    "preview": "package widgets\n\nimport (\n\t\"fmt\"\n\t\"image\"\n\t\"strings\"\n\n\t. \"github.com/gizak/termui/v3\"\n\trw \"github.com/mattn/go-runewidth"
  }
]

About this extraction

This page contains the full source code of the gizak/termui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 54 files (106.9 KB), approximately 36.2k tokens, and a symbol index with 296 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!