diff options
author | Caleb Bassi <calebjbassi@gmail.com> | 2018-03-29 15:48:43 -0700 |
---|---|---|
committer | Caleb Bassi <calebjbassi@gmail.com> | 2018-03-29 15:48:43 -0700 |
commit | dc41895011e95d07b3929506cd557d80a4c871ae (patch) | |
tree | cbb7df1da51e39c50897750d98ed2ef1456e3bfb /termui | |
parent | dc8cc5aa4db5cc08ae01b2ce48b6d310b98e858f (diff) |
Move termui to seperate repo
Diffstat (limited to 'termui')
-rw-r--r-- | termui/block.go | 100 | ||||
-rw-r--r-- | termui/block_common.go | 18 | ||||
-rw-r--r-- | termui/block_windows.go | 18 | ||||
-rw-r--r-- | termui/buffer.go | 99 | ||||
-rw-r--r-- | termui/colors.go | 49 | ||||
-rw-r--r-- | termui/events.go | 204 | ||||
-rw-r--r-- | termui/gauge.go | 51 | ||||
-rw-r--r-- | termui/grid.go | 66 | ||||
-rw-r--r-- | termui/init.go | 26 | ||||
-rw-r--r-- | termui/linegraph.go | 126 | ||||
-rw-r--r-- | termui/render.go | 40 | ||||
-rw-r--r-- | termui/sparkline.go | 77 | ||||
-rw-r--r-- | termui/table.go | 179 | ||||
-rw-r--r-- | termui/utils.go | 24 |
14 files changed, 0 insertions, 1077 deletions
diff --git a/termui/block.go b/termui/block.go deleted file mode 100644 index 4695f25..0000000 --- a/termui/block.go +++ /dev/null @@ -1,100 +0,0 @@ -package termui - -import ( - "image" -) - -// Block is a base struct for all other upper level widgets. -type Block struct { - Grid image.Rectangle - X int // largest X value in the inner square - Y int // largest Y value in the inner square - XOffset int // the X position of the widget on the terminal - YOffset int // the Y position of the widget on the terminal - Label string - BorderFg Color - BorderBg Color - LabelFg Color - LabelBg Color - Fg Color - Bg Color -} - -// NewBlock returns a *Block which inherits styles from the current theme. -func NewBlock() *Block { - return &Block{ - Fg: Theme.Fg, - Bg: Theme.Bg, - BorderFg: Theme.BorderFg, - BorderBg: Theme.BorderBg, - LabelFg: Theme.LabelFg, - LabelBg: Theme.LabelBg, - } -} - -func (self *Block) drawBorder(buf *Buffer) { - x := self.X + 1 - y := self.Y + 1 - - // draw lines - buf.Merge(NewFilledBuffer(0, 0, x, 1, Cell{HORIZONTAL_LINE, self.BorderFg, self.BorderBg})) - buf.Merge(NewFilledBuffer(0, y, x, y+1, Cell{HORIZONTAL_LINE, self.BorderFg, self.BorderBg})) - buf.Merge(NewFilledBuffer(0, 0, 1, y+1, Cell{VERTICAL_LINE, self.BorderFg, self.BorderBg})) - buf.Merge(NewFilledBuffer(x, 0, x+1, y+1, Cell{VERTICAL_LINE, self.BorderFg, self.BorderBg})) - - // draw corners - buf.SetCell(0, 0, Cell{TOP_LEFT, self.BorderFg, self.BorderBg}) - buf.SetCell(x, 0, Cell{TOP_RIGHT, self.BorderFg, self.BorderBg}) - buf.SetCell(0, y, Cell{BOTTOM_LEFT, self.BorderFg, self.BorderBg}) - buf.SetCell(x, y, Cell{BOTTOM_RIGHT, self.BorderFg, self.BorderBg}) -} - -func (self *Block) drawLabel(buf *Buffer) { - r := MaxString(self.Label, (self.X-3)-1) - buf.SetString(3, 0, r, self.LabelFg, self.LabelBg) - if self.Label == "" { - return - } - c := Cell{' ', self.Fg, self.Bg} - buf.SetCell(2, 0, c) - if len(self.Label)+3 < self.X { - buf.SetCell(len(self.Label)+3, 0, c) - } else { - buf.SetCell(self.X-1, 0, c) - } -} - -// Resize computes Height, Width, XOffset, and YOffset given terminal dimensions. -func (self *Block) Resize(termWidth, termHeight, termCols, termRows int) { - self.X = int((float64(self.Grid.Dx())/float64(termCols))*float64(termWidth)) - 2 - self.Y = int((float64(self.Grid.Dy())/float64(termRows))*float64(termHeight)) - 2 - self.XOffset = int((float64(self.Grid.Min.X) / float64(termCols)) * float64(termWidth)) - self.YOffset = int((float64(self.Grid.Min.Y) / float64(termRows)) * float64(termHeight)) -} - -// SetGrid create a rectangle representing the block's dimensions in the grid. -func (self *Block) SetGrid(c0, r0, c1, r1 int) { - self.Grid = image.Rect(c0, r0, c1, r1) -} - -// GetXOffset implements Bufferer interface. -func (self *Block) GetXOffset() int { - return self.XOffset -} - -// GetYOffset implements Bufferer interface. -func (self *Block) GetYOffset() int { - return self.YOffset -} - -// Buffer implements Bufferer interface and draws background, border, and borderlabel. -func (self *Block) Buffer() *Buffer { - buf := NewBuffer() - buf.SetAreaXY(self.X+2, self.Y+2) - buf.Fill(Cell{' ', ColorDefault, self.Bg}) - - self.drawBorder(buf) - self.drawLabel(buf) - - return buf -} diff --git a/termui/block_common.go b/termui/block_common.go deleted file mode 100644 index aeef1c8..0000000 --- a/termui/block_common.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build !windows - -package termui - -const ( - TOP_RIGHT = '┐' - VERTICAL_LINE = '│' - HORIZONTAL_LINE = '─' - TOP_LEFT = '┌' - BOTTOM_RIGHT = '┘' - BOTTOM_LEFT = '└' - VERTICAL_LEFT = '┤' - VERTICAL_RIGHT = '├' - HORIZONTAL_DOWN = '┬' - HORIZONTAL_UP = '┴' - QUOTA_LEFT = '«' - QUOTA_RIGHT = '»' -) diff --git a/termui/block_windows.go b/termui/block_windows.go deleted file mode 100644 index af0307f..0000000 --- a/termui/block_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build windows - -package termui - -const ( - TOP_RIGHT = '+' - VERTICAL_LINE = '|' - HORIZONTAL_LINE = '-' - TOP_LEFT = '+' - BOTTOM_RIGHT = '+' - BOTTOM_LEFT = '+' - VERTICAL_LEFT = '+' - VERTICAL_RIGHT = '+' - HORIZONTAL_DOWN = '+' - HORIZONTAL_UP = '+' - QUOTA_LEFT = '<' - QUOTA_RIGHT = '>' -) diff --git a/termui/buffer.go b/termui/buffer.go deleted file mode 100644 index 9faabaf..0000000 --- a/termui/buffer.go +++ /dev/null @@ -1,99 +0,0 @@ -package termui - -import ( - "image" -) - -// Cell is a rune with assigned Fg and Bg. -type Cell struct { - Ch rune - Fg Color - Bg Color -} - -// Buffer is a renderable rectangle cell data container. -type Buffer struct { - Area image.Rectangle // selected drawing area - CellMap map[image.Point]Cell -} - -// NewCell returne a new Cell given all necessary fields. -func NewCell(ch rune, Fg, Bg Color) Cell { - return Cell{ch, Fg, Bg} -} - -// NewBuffer returns a new empty Buffer. -func NewBuffer() *Buffer { - return &Buffer{ - CellMap: make(map[image.Point]Cell), - Area: image.Rectangle{}, - } -} - -// NewFilledBuffer returns a new Buffer filled with the given Cell. -func NewFilledBuffer(x0, y0, x1, y1 int, c Cell) *Buffer { - buf := NewBuffer() - buf.Area.Min = image.Pt(x0, y0) - buf.Area.Max = image.Pt(x1, y1) - buf.Fill(c) - return buf -} - -// SetCell assigns a Cell to (x,y). -func (self *Buffer) SetCell(x, y int, c Cell) { - self.CellMap[image.Pt(x, y)] = c -} - -// SetString assigns a string to a Buffer starting at (x,y). -func (self *Buffer) SetString(x, y int, s string, fg, bg Color) { - for i, char := range s { - self.SetCell(x+i, y, Cell{char, fg, bg}) - } -} - -// At returns the cell at (x,y). -func (self *Buffer) At(x, y int) Cell { - return self.CellMap[image.Pt(x, y)] -} - -// SetArea assigns a new rect area to self. -func (self *Buffer) SetArea(r image.Rectangle) { - self.Area.Max = r.Max - self.Area.Min = r.Min -} - -// SetAreaXY sets the Buffer bounds from (0,0) to (x,y). -func (self *Buffer) SetAreaXY(x, y int) { - self.Area.Min.Y = 0 - self.Area.Min.X = 0 - self.Area.Max.Y = y - self.Area.Max.X = x -} - -// Merge merges the given buffers onto the current Buffer. -func (self *Buffer) Merge(bs ...*Buffer) { - for _, buf := range bs { - for p, c := range buf.CellMap { - self.SetCell(p.X, p.Y, c) - } - self.SetArea(self.Area.Union(buf.Area)) - } -} - -// MergeWithOffset merges a Buffer onto another with an offset. -func (self *Buffer) MergeWithOffset(buf *Buffer, xOffset, yOffset int) { - for p, c := range buf.CellMap { - self.SetCell(p.X+xOffset, p.Y+yOffset, c) - } - rect := image.Rect(xOffset, yOffset, buf.Area.Max.X+xOffset, buf.Area.Max.Y+yOffset) - self.SetArea(self.Area.Union(rect)) -} - -// Fill fills the Buffer with a Cell. -func (self *Buffer) Fill(c Cell) { - for x := self.Area.Min.X; x < self.Area.Max.X; x++ { - for y := self.Area.Min.Y; y < self.Area.Max.Y; y++ { - self.SetCell(x, y, c) - } - } -} diff --git a/termui/colors.go b/termui/colors.go deleted file mode 100644 index 3af2469..0000000 --- a/termui/colors.go +++ /dev/null @@ -1,49 +0,0 @@ -package termui - -// Color is an integer in the range -1 to 255. -type Color int - -// ColorDefault = clear -const ColorDefault = -1 - -// Copied from termbox. Attributes that can be bitwise OR'ed with a color. -const ( - AttrBold Color = 1 << (iota + 9) - AttrUnderline - AttrReverse -) - -// Theme is assigned to the current theme. -var Theme = DefaultTheme - -// DefaultTheme implements a generic set of colors to use by default. -var DefaultTheme = Colorscheme{ - Fg: 7, - Bg: -1, - - LabelFg: 7, - LabelBg: -1, - BorderFg: 6, - BorderBg: -1, - - Sparkline: 4, - LineGraph: 0, - TableCursor: 4, - GaugeColor: 7, -} - -// A Colorscheme represents the current look-and-feel of the dashboard. -type Colorscheme struct { - Fg Color - Bg Color - - LabelFg Color - LabelBg Color - BorderFg Color - BorderBg Color - - Sparkline Color - LineGraph Color - TableCursor Color - GaugeColor Color -} diff --git a/termui/events.go b/termui/events.go deleted file mode 100644 index 3bc5459..0000000 --- a/termui/events.go +++ /dev/null @@ -1,204 +0,0 @@ -package termui - -import ( - "strconv" - - tb "github.com/nsf/termbox-go" -) - -/* -here's the list of events which you can assign handlers too using the `On` function: - mouse events: - <MouseLeft> <MouseRight> <MouseMiddle> - <MouseWheelUp> <MouseWheelDown> - keyboard events: - any uppercase or lowercase letter or a set of two letters like j or jj or J or JJ - <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> -*/ - -var eventStream = EventStream{ - make(map[string]func(Event)), - "", - make(chan bool, 1), - make(chan tb.Event), -} - -type EventStream struct { - eventHandlers map[string]func(Event) - prevKey string // previous keypress - stopLoop chan bool - eventQueue chan tb.Event // list of events from termbox -} - -// Event is a copy of termbox.Event that only contains the fields we need. -type Event struct { - Key string - Width int - Height int - MouseX int - MouseY int -} - -// handleEvent calls the approriate callback function if there is one. -func handleEvent(e tb.Event) { - if e.Type == tb.EventError { - panic(e.Err) - } - - ne := convertTermboxEvent(e) - - if val, ok := eventStream.eventHandlers[ne.Key]; ok { - val(ne) - eventStream.prevKey = "" - } else { // check if the last 2 keys form a key combo with a handler - // if this is a keyboard event and the previous event was unhandled - if e.Type == tb.EventKey && eventStream.prevKey != "" { - combo := eventStream.prevKey + ne.Key - if val, ok := eventStream.eventHandlers[combo]; ok { - ne.Key = combo - val(ne) - eventStream.prevKey = "" - } else { - eventStream.prevKey = ne.Key - } - } else { - eventStream.prevKey = ne.Key - } - } -} - -// Loop gets events from termbox and passes them off to handleEvent. -// Stops when StopLoop is called. -func Loop() { - go func() { - for { - eventStream.eventQueue <- tb.PollEvent() - } - }() - - for { - select { - case <-eventStream.stopLoop: - return - case e := <-eventStream.eventQueue: - handleEvent(e) - } - } -} - -// StopLoop stops the event loop. -func StopLoop() { - eventStream.stopLoop <- true -} - -// On assigns event names to their handlers. Takes a string, strings, or a slice of strings, and a function. -func On(things ...interface{}) { - function := things[len(things)-1].(func(Event)) - for _, thing := range things { - if value, ok := thing.(string); ok { - eventStream.eventHandlers[value] = function - } - if value, ok := thing.([]string); ok { - for _, name := range value { - eventStream.eventHandlers[name] = function - } - } - } -} - -// convertTermboxKeyValue 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 convertTermboxKeyValue(e tb.Event) string { - k := string(e.Ch) - pre := "" - mod := "" - - if e.Mod == tb.ModAlt { - mod = "<M-" - } - if e.Ch == 0 { - if e.Key > 0xFFFF-12 { - k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">" - } else if e.Key > 0xFFFF-25 { - ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"} - k = ks[0xFFFF-int(e.Key)-12] - } - - if e.Key <= 0x7F { - pre = "<C-" - k = string('a' - 1 + int(e.Key)) - kmap := map[tb.Key][2]string{ - tb.KeyCtrlSpace: {"C-", "<space>"}, - tb.KeyBackspace: {"", "<backspace>"}, - tb.KeyTab: {"", "<tab>"}, - tb.KeyEnter: {"", "<enter>"}, - tb.KeyEsc: {"", "<escape>"}, - tb.KeyCtrlBackslash: {"C-", "\\"}, - tb.KeyCtrlSlash: {"C-", "/"}, - tb.KeySpace: {"", "<space>"}, - tb.KeyCtrl8: {"C-", "8"}, - } - if sk, ok := kmap[e.Key]; ok { - pre = sk[0] - k = sk[1] - } - } - } - - if pre != "" { - k += ">" - } - - return pre + mod + k -} - -func convertTermboxMouseValue(e tb.Event) string { - switch e.Key { - case tb.MouseLeft: - return "<MouseLeft>" - case tb.MouseMiddle: - return "<MouseMiddle>" - case tb.MouseRight: - return "<MouseRight>" - case tb.MouseWheelUp: - return "<MouseWheelUp>" - case tb.MouseWheelDown: - return "<MouseWheelDown>" - case tb.MouseRelease: - return "<MouseRelease>" - } - return "" -} - -// convertTermboxEvent turns a termbox event into a termui event. -func convertTermboxEvent(e tb.Event) Event { - var ne Event - - switch e.Type { - case tb.EventKey: - ne = Event{ - Key: convertTermboxKeyValue(e), - } - case tb.EventMouse: - ne = Event{ - Key: convertTermboxMouseValue(e), - MouseX: e.MouseX, - MouseY: e.MouseY, - } - case tb.EventResize: - ne = Event{ - Key: "<resize>", - Width: e.Width, - Height: e.Height, - } - } - - return ne -} diff --git a/termui/gauge.go b/termui/gauge.go deleted file mode 100644 index a5b3b2a..0000000 --- a/termui/gauge.go +++ /dev/null @@ -1,51 +0,0 @@ -package termui - -import ( - "strconv" -) - -// Gauge is a progress bar like widget. -type Gauge struct { - *Block - Percent int - GaugeColor Color - Description string -} - -// NewGauge return a new gauge with current theme. -func NewGauge() *Gauge { - return &Gauge{ - Block: NewBlock(), - GaugeColor: Theme.GaugeColor, - } -} - -// Buffer implements Bufferer interface. -func (self *Gauge) Buffer() *Buffer { - buf := self.Block.Buffer() - - // plot bar - width := self.Percent * self.X / 100 - for y := 1; y <= self.Y; y++ { - for x := 1; x <= width; x++ { - buf.SetCell(x, y, Cell{' ', self.GaugeColor, self.GaugeColor}) - } - } - - // plot percentage - s := strconv.Itoa(self.Percent) + "%" + self.Description - s = MaxString(s, self.X) - y := (self.Y + 1) / 2 - x := ((self.X - len(s)) + 1) / 2 - for i, char := range s { - bg := self.Bg - fg := self.Fg - if x+i < width { - fg = self.GaugeColor - bg = AttrReverse - } - buf.SetCell(1+x+i, y, Cell{char, fg, bg}) - } - - return buf -} diff --git a/termui/grid.go b/termui/grid.go deleted file mode 100644 index 58b4839..0000000 --- a/termui/grid.go +++ /dev/null @@ -1,66 +0,0 @@ -package termui - -var Body *Grid - -// GridBufferer introduces a Bufferer that can be manipulated by Grid. -type GridBufferer interface { - Bufferer - Resize(int, int, int, int) - SetGrid(int, int, int, int) -} - -// Grid holds widgets and information about terminal dimensions. -// Widgets are adjusted and rendered through the grid. -type Grid struct { - Widgets []GridBufferer - Width int - Height int - Cols int - Rows int -} - -// NewGrid creates an empty Grid. -func NewGrid() *Grid { - return &Grid{} -} - -// Set assigns a widget and its grid dimensions to Grid. -func (self *Grid) Set(x0, y0, x1, y1 int, widget GridBufferer) { - if widget == nil { - return - } - if x1 <= x0 || y1 <= y0 { - panic("Invalid widget coordinates") - } - - widget.SetGrid(x0, y0, x1, y1) - widget.Resize(self.Width, self.Height, self.Cols, self.Rows) - - self.Widgets = append(self.Widgets, widget) -} - -// Resize resizes each widget in the grid. -func (self *Grid) Resize() { - for _, w := range self.Widgets { - w.Resize(self.Width, self.Height, self.Cols, self.Rows) - } -} - -// Buffer implements the Bufferer interface by merging each widget in Grid into one buffer. -func (self *Grid) Buffer() *Buffer { - buf := NewFilledBuffer(0, 0, self.Width, self.Height, Cell{' ', ColorDefault, Theme.Bg}) - for _, w := range self.Widgets { - buf.MergeWithOffset(w.Buffer(), w.GetXOffset(), w.GetYOffset()) - } - return buf -} - -// GetXOffset implements Bufferer interface. -func (self *Grid) GetXOffset() int { - return 0 -} - -// GetYOffset implements Bufferer interface. -func (self *Grid) GetYOffset() int { - return 0 -} diff --git a/termui/init.go b/termui/init.go deleted file mode 100644 index 764de2b..0000000 --- a/termui/init.go +++ /dev/null @@ -1,26 +0,0 @@ -package termui - -import ( - tb "github.com/nsf/termbox-go" -) - -// Init initializes termui library. This function should be called before any others. -// After initialization, the library must be finalized by 'Close' function. -func Init() error { - if err := tb.Init(); err != nil { - return err - } - tb.SetInputMode(tb.InputEsc | tb.InputMouse) - tb.SetOutputMode(tb.Output256) - - Body = NewGrid() - Body.Width, Body.Height = tb.Size() - - return nil -} - -// Close finalizes termui library. -// It should be called after successful initialization when termui's functionality isn't required anymore. -func Close() { - tb.Close() -} diff --git a/termui/linegraph.go b/termui/linegraph.go deleted file mode 100644 index cc71e8f..0000000 --- a/termui/linegraph.go +++ /dev/null @@ -1,126 +0,0 @@ -package termui - -import ( - "fmt" - "sort" - - drawille "github.com/cjbassi/drawille-go" -) - -// LineGraph implements a line graph of data points. -type LineGraph struct { - *Block - Data map[string][]float64 - LineColor map[string]Color - Zoom int - - DefaultLineColor Color -} - -// NewLineGraph returns a new LineGraph with current theme. -func NewLineGraph() *LineGraph { - return &LineGraph{ - Block: NewBlock(), - Data: make(map[string][]float64), - LineColor: make(map[string]Color), - Zoom: 5, - - DefaultLineColor: Theme.LineGraph, - } -} - -// Buffer implements Bufferer interface. -func (self *LineGraph) Buffer() *Buffer { - buf := self.Block.Buffer() - // we render each data point on to the canvas then copy over the braille to the buffer at the end - // fyi braille characters have 2x4 dots for each character - c := drawille.NewCanvas() - // used to keep track of the braille colors until the end when we render the braille to the buffer - colors := make([][]Color, self.X+2) - for i := range colors { - colors[i] = make([]Color, self.Y+2) - } - - // sort the series so that overlapping data will overlap the same way each time - seriesList := make([]string, len(self.Data)) - i := 0 - for seriesName := range self.Data { - seriesList[i] = seriesName - i++ - } - sort.Strings(seriesList) - - // draw lines in reverse order so that the first color defined in the colorscheme is on top - for i := len(seriesList) - 1; i >= 0; i-- { - seriesName := seriesList[i] - seriesData := self.Data[seriesName] - seriesLineColor, ok := self.LineColor[seriesName] - if !ok { - seriesLineColor = self.DefaultLineColor - } - - // coordinates of last point - lastY, lastX := -1, -1 - // assign colors to `colors` and lines/points to the canvas - for i := len(seriesData) - 1; i >= 0; i-- { - x := ((self.X + 1) * 2) - 1 - (((len(seriesData) - 1) - i) * self.Zoom) - y := ((self.Y + 1) * 4) - 1 - int((float64((self.Y)*4)-1)*(seriesData[i]/100)) - if x < 0 { - // render the line to the last point up to the wall - if x > 0-self.Zoom { - for _, p := range drawille.Line(lastX, lastY, x, y) { - if p.X > 0 { - c.Set(p.X, p.Y) - colors[p.X/2][p.Y/4] = seriesLineColor - } - } - } - break - } - if lastY == -1 { // if this is the first point - c.Set(x, y) - colors[x/2][y/4] = seriesLineColor - } else { - c.DrawLine(lastX, lastY, x, y) - for _, p := range drawille.Line(lastX, lastY, x, y) { - colors[p.X/2][p.Y/4] = seriesLineColor - } - } - lastX, lastY = x, y - } - - // copy braille and colors to buffer - for y, line := range c.Rows(c.MinX(), c.MinY(), c.MaxX(), c.MaxY()) { - for x, char := range line { - x /= 3 // idk why but it works - if x == 0 { - continue - } - if char != 10240 { // empty braille character - buf.SetCell(x, y, Cell{char, colors[x][y], self.Bg}) - } - } - } - } - - // renders key ontop - for j, seriesName := range seriesList { - // sorts lines again - seriesData := self.Data[seriesName] - seriesLineColor, ok := self.LineColor[seriesName] - if !ok { - seriesLineColor = self.DefaultLineColor - } - - // render key ontop, but let braille be drawn over space characters - str := fmt.Sprintf("%s %3.0f%%", seriesName, seriesData[len(seriesData)-1]) - for k, char := range str { - if char != ' ' { - buf.SetCell(3+k, j+2, Cell{char, seriesLineColor, self.Bg}) - } - } - - } - - return buf -} diff --git a/termui/render.go b/termui/render.go deleted file mode 100644 index 52bcdf3..0000000 --- a/termui/render.go +++ /dev/null @@ -1,40 +0,0 @@ -package termui - -import ( - "sync" - - tb "github.com/nsf/termbox-go" -) - -// Bufferer should be implemented by all renderable components. -type Bufferer interface { - Buffer() *Buffer - GetXOffset() int - GetYOffset() int -} - -// Render renders all Bufferers in the given order to termbox, then asks termbox to print the screen. -func Render(bs ...Bufferer) { - var wg sync.WaitGroup - for _, b := range bs { - wg.Add(1) - go func(b Bufferer) { - defer wg.Done() - buf := b.Buffer() - // set cells in buf - for p, c := range buf.CellMap { - if p.In(buf.Area) { - tb.SetCell(p.X+b.GetXOffset(), p.Y+b.GetYOffset(), c.Ch, tb.Attribute(c.Fg)+1, tb.Attribute(c.Bg)+1) - } - } - }(b) - } - - wg.Wait() - tb.Flush() -} - -// Clear clears the screen with the default Bg color. -func Clear() { - tb.Clear(tb.ColorDefault+1, tb.Attribute(Theme.Bg)+1) -} diff --git a/termui/sparkline.go b/termui/sparkline.go deleted file mode 100644 index 8cefff8..0000000 --- a/termui/sparkline.go +++ /dev/null @@ -1,77 +0,0 @@ -package termui - -var SPARKS = [8]rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} - -// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers. -type Sparkline struct { - Data []int - Title1 string - Title2 string - TitleColor Color - LineColor Color -} - -// Sparklines is a renderable widget which groups together the given sparklines. -type Sparklines struct { - *Block - Lines []*Sparkline -} - -// Add appends a given Sparkline to the *Sparklines. -func (self *Sparklines) Add(sl Sparkline) { - self.Lines = append(self.Lines, &sl) -} - -// NewSparkline returns an unrenderable single sparkline that intended to be added into a Sparklines. -func NewSparkline() *Sparkline { - return &Sparkline{ - TitleColor: Theme.Fg, - LineColor: Theme.Sparkline, - } -} - -// NewSparklines return a new *Sparklines with given Sparklines, you can always add a new Sparkline later. -func NewSparklines(ss ...*Sparkline) *Sparklines { - return &Sparklines{ - Block: NewBlock(), - Lines: ss, - } -} - -// Buffer implements Bufferer interface. -func (self *Sparklines) Buffer() *Buffer { - buf := self.Block.Buffer() - - lc := len(self.Lines) // lineCount - - // renders each sparkline and its titles - for i, line := range self.Lines { - - // prints titles - title1Y := 2 + (self.Y/lc)*i - title2Y := (2 + (self.Y/lc)*i) + 1 - title1 := MaxString(line.Title1, self.X) - title2 := MaxString(line.Title2, self.X) - buf.SetString(1, title1Y, title1, line.TitleColor|AttrBold, self.Bg) - buf.SetString(1, title2Y, title2, line.TitleColor|AttrBold, self.Bg) - - sparkY := (self.Y / lc) * (i + 1) - // finds max data in current view used for relative heights - max := 1 - for i := len(line.Data) - 1; i >= 0 && self.X-((len(line.Data)-1)-i) >= 1; i-- { - if line.Data[i] > max { - max = line.Data[i] - } - } - // prints sparkline - for x := self.X; x >= 1; x-- { - char := SPARKS[0] - if (self.X - x) < len(line.Data) { - char = SPARKS[int((float64(line.Data[(len(line.Data)-1)-(self.X-x)])/float64(max))*7)] - } - buf.SetCell(x, sparkY, Cell{char, line.LineColor, self.Bg}) - } - } - - return buf -} diff --git a/termui/table.go b/termui/table.go deleted file mode 100644 index e24031b..0000000 --- a/termui/table.go +++ /dev/null @@ -1,179 +0,0 @@ -package termui - -import ( - "strings" -) - -// Table tracks all the attributes of a Table instance -type Table struct { - *Block - Header []string - Rows [][]string - ColWidths []int - CellXPos []int // column position - Gap int // gap between columns - Cursor Color - UniqueCol int // the column used to identify the selected item - SelectedItem string // used to keep the cursor on the correct item if the data changes - SelectedRow int - TopRow int // used to indicate where in the table we are scrolled at - ColResizer func() // for widgets that inherit a Table and want to overload the ColResize method -} - -// NewTable returns a new Table instance -func NewTable() *Table { - self := &Table{ - Block: NewBlock(), - Cursor: Theme.TableCursor, - SelectedRow: 0, - TopRow: 0, - UniqueCol: 0, - } - self.ColResizer = self.ColResize - return self -} - -// ColResize is the default column resizer, but can be overriden. -// ColResize calculates the width of each column. -func (self *Table) ColResize() { - // calculate gap size based on total width - self.Gap = 3 - if self.X < 50 { - self.Gap = 1 - } else if self.X < 75 { - self.Gap = 2 - } - - cur := 0 - for _, w := range self.ColWidths { - cur += self.Gap - self.CellXPos = append(self.CellXPos, cur) - cur += w - } -} - -// Buffer implements the Bufferer interface. -func (self *Table) Buffer() *Buffer { - buf := self.Block.Buffer() - - // removes gap at the bottom of the current view if there is one - if self.TopRow > len(self.Rows)-(self.Y-1) { - self.TopRow = len(self.Rows) - (self.Y - 1) - } - - self.ColResizer() - - // prints header - for i, width := range self.ColWidths { - if width == 0 { - break - } - r := MaxString(self.Header[i], self.X-6) - buf.SetString(self.CellXPos[i], 1, r, self.Fg|AttrBold, self.Bg) - } - - // prints each row - for rowNum := self.TopRow; rowNum < self.TopRow+self.Y-1 && rowNum < len(self.Rows); rowNum++ { - row := self.Rows[rowNum] - y := (rowNum + 2) - self.TopRow - - // prints cursor - bg := self.Bg - if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) { - bg = self.Cursor - for _, width := range self.ColWidths { - if width == 0 { - break - } - buf.SetString(1, y, strings.Repeat(" ", self.X), self.Fg, bg) - } - self.SelectedItem = row[self.UniqueCol] - self.SelectedRow = rowNum - } - - // prints each col of the row - for i, width := range self.ColWidths { - if width == 0 { - break - } - r := MaxString(row[i], self.X-6) - buf.SetString(self.CellXPos[i], y, r, self.Fg, bg) - } - } - - return buf -} - -///////////////////////////////////////////////////////////////////////////////// -// Cursor Movement // -///////////////////////////////////////////////////////////////////////////////// - -// calcPos is used to calculate the cursor position and the current view. -func (self *Table) calcPos() { - self.SelectedItem = "" - - if self.SelectedRow < 0 { - self.SelectedRow = 0 - } - if self.SelectedRow < self.TopRow { - self.TopRow = self.SelectedRow - } - - if self.SelectedRow > len(self.Rows)-1 { - self.SelectedRow = len(self.Rows) - 1 - } - if self.SelectedRow > self.TopRow+(self.Y-2) { - self.TopRow = self.SelectedRow - (self.Y - 2) - } -} - -func (self *Table) Up() { - self.SelectedRow -= 1 - self.calcPos() -} - -func (self *Table) Down() { - self.SelectedRow += 1 - self.calcPos() -} - -func (self *Table) Top() { - self.SelectedRow = 0 - self.calcPos() -} - -func (self *Table) Bottom() { - self.SelectedRow = len(self.Rows) - 1 - self.calcPos() -} - -// The number of lines in a page is equal to the height of the widgeself. - -func (self *Table) HalfPageUp() { - self.SelectedRow = self.SelectedRow - (self.Y-2)/2 - self.calcPos() -} - -func (self *Table) HalfPageDown() { - self.SelectedRow = self.SelectedRow + (self.Y-2)/2 - self.calcPos() -} - -func (self *Table) PageUp() { - self.SelectedRow -= (self.Y - 2) - self.calcPos() -} - -func (self *Table) PageDown() { - self.SelectedRow += (self.Y - 2) - self.calcPos() |