From 21aed4b1eafd6cd28b9deef8084c009c38dd8f5f Mon Sep 17 00:00:00 2001 From: Caleb Bassi Date: Sat, 24 Feb 2018 21:35:57 -0800 Subject: [PATCH] Removed gotop specific stuff from termui --- README.md | 2 - gotop.go | 7 +- termui/colors.go | 8 +-- termui/gauge.go | 16 ++--- termui/list.go | 44 ------------ termui/sparkline.go | 1 - termui/table.go | 168 +++++++++++++++++++++----------------------- widgets/proc.go | 43 ++++++++++++ widgets/temp.go | 42 +++++++++-- 9 files changed, 174 insertions(+), 157 deletions(-) delete mode 100644 termui/list.go diff --git a/README.md b/README.md index 1884bea..60935e9 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,6 @@ Feel free to add a new one. You can use 256 colors, bold, underline, and reverse - termui buffers should ignore setting characters outside the widget area - ignore writes or give an error? - termui Blocks should be indexed at 0, and maybe change their X and Y variables too - - remove gotop unique logic from list and table - - turn column width logic into a function - try to get drawille fork merged upstream - more documentation - Draw borders and label after other stuff diff --git a/gotop.go b/gotop.go index 9b1ad4f..e498d96 100644 --- a/gotop.go +++ b/gotop.go @@ -122,9 +122,7 @@ func termuiColors() { ui.Theme.TableCursor = ui.Color(colorscheme.ProcCursor) ui.Theme.Sparkline = ui.Color(colorscheme.Sparkline) - ui.Theme.BarColor = ui.Color(colorscheme.DiskBar) - ui.Theme.TempLow = ui.Color(colorscheme.TempLow) - ui.Theme.TempHigh = ui.Color(colorscheme.TempHigh) + ui.Theme.GaugeColor = ui.Color(colorscheme.DiskBar) } func widgetColors() { @@ -138,6 +136,9 @@ func widgetColors() { LineColor[fmt.Sprintf("CPU%d", i+1)] = ui.Color(colorscheme.CPULines[i]) } cpu.LineColor = LineColor + + temp.TempLow = ui.Color(colorscheme.TempLow) + temp.TempHigh = ui.Color(colorscheme.TempHigh) } func main() { diff --git a/termui/colors.go b/termui/colors.go index d8dc8bf..2fdacd3 100644 --- a/termui/colors.go +++ b/termui/colors.go @@ -28,9 +28,7 @@ var DefaultTheme = Colorscheme{ Sparkline: 4, LineGraph: -1, TableCursor: 4, - BarColor: 7, - TempLow: 2, - TempHigh: 1, + GaugeColor: 7, } // A Colorscheme represents the current look-and-feel of the dashboard. @@ -46,7 +44,5 @@ type Colorscheme struct { Sparkline Color LineGraph Color TableCursor Color - BarColor Color - TempLow Color - TempHigh Color + GaugeColor Color } diff --git a/termui/gauge.go b/termui/gauge.go index 11235f4..5667686 100644 --- a/termui/gauge.go +++ b/termui/gauge.go @@ -7,18 +7,16 @@ import ( // Gauge is a progress bar like widget. type Gauge struct { *Block - Percent int - BarColor Color - PercentColor Color - Description string + Percent int + GaugeColor Color + Description string } // NewGauge return a new gauge with current theme. func NewGauge() *Gauge { return &Gauge{ - Block: NewBlock(), - PercentColor: Theme.Fg, - BarColor: Theme.BarColor, + Block: NewBlock(), + GaugeColor: Theme.GaugeColor, } } @@ -30,7 +28,7 @@ func (g *Gauge) Buffer() *Buffer { width := g.Percent * g.X / 100 for y := 1; y <= g.Y; y++ { for x := 1; x <= width; x++ { - buf.SetCell(x, y, Cell{' ', g.BarColor, g.BarColor}) + buf.SetCell(x, y, Cell{' ', g.GaugeColor, g.GaugeColor}) } } @@ -45,7 +43,7 @@ func (g *Gauge) Buffer() *Buffer { bg := g.Bg fg := g.Fg if x+i < width { - fg = g.BarColor + fg = g.GaugeColor bg = AttrReverse } buf.SetCell(1+x+i, y, Cell{char, fg, bg}) diff --git a/termui/list.go b/termui/list.go deleted file mode 100644 index f1908ed..0000000 --- a/termui/list.go +++ /dev/null @@ -1,44 +0,0 @@ -package termui - -import ( - "fmt" -) - -// BarChart creates multiple bars in a widget: -type List struct { - *Block - TextColor Color - Data []int - DataLabels []string - Threshold int -} - -// NewBarChart returns a new *BarChart with current theme. -func NewList() *List { - return &List{ - Block: NewBlock(), - TextColor: Theme.Fg, - } -} - -// Buffer implements Bufferer interface. -func (bc *List) Buffer() *Buffer { - buf := bc.Block.Buffer() - - for y, text := range bc.DataLabels { - if y+1 > bc.Y { - break - } - - fg := Theme.TempLow - if bc.Data[y] >= bc.Threshold { - fg = Theme.TempHigh - } - - s := MaxString(text, (bc.X - 4)) - buf.SetString(1, y+1, s, Theme.Fg, bc.Bg) - buf.SetString(bc.X-2, y+1, fmt.Sprintf("%dC", bc.Data[y]), fg, bc.Bg) - } - - return buf -} diff --git a/termui/sparkline.go b/termui/sparkline.go index c9bcb0d..5de952f 100644 --- a/termui/sparkline.go +++ b/termui/sparkline.go @@ -8,7 +8,6 @@ type Sparkline struct { Title1 string Title2 string TitleColor Color - Total int LineColor Color } diff --git a/termui/table.go b/termui/table.go index 883cda7..d18bdc6 100644 --- a/termui/table.go +++ b/termui/table.go @@ -1,36 +1,53 @@ package termui import ( - "os/exec" "strings" ) // Table tracks all the attributes of a Table instance type Table struct { *Block - Header []string - Rows [][]string - Fg Color - Bg Color - Cursor Color - // the unique column used to keep track of which process we're one - // either the PID column or Command column depending on if processes are grouped - UniqueCol int - pid string // used to keep the cursor on the correct process after each update - selected int // selected row - topRow int // top process in current view + Header []string + Rows [][]string + ColWidths []int + Cp []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() } // NewTable returns a new Table instance func NewTable() *Table { - return &Table{ - Block: NewBlock(), - Fg: Theme.Fg, - Bg: Theme.Bg, - Cursor: Theme.TableCursor, - selected: 0, - topRow: 0, - UniqueCol: 0, + t := &Table{ + Block: NewBlock(), + Cursor: Theme.TableCursor, + SelectedRow: 0, + TopRow: 0, + UniqueCol: 0, + } + t.ColResizer = t.ColResize + return t +} + +// ColResize is the default column resizer, but can be overriden. +func (t *Table) ColResize() { + // calculate gap size based on total width + t.Gap = 3 + if t.X < 50 { + t.Gap = 1 + } else if t.X < 75 { + t.Gap = 2 + } + + cur := 0 + for _, w := range t.ColWidths { + cur += t.Gap + t.Cp = append(t.Cp, cur) + cur += w } } @@ -38,64 +55,49 @@ func NewTable() *Table { func (t *Table) Buffer() *Buffer { buf := t.Block.Buffer() - if t.topRow > len(t.Rows)-(t.Y-1) { - t.topRow = len(t.Rows) - (t.Y - 1) + // makes sure there isn't a gap at the bottom of the table view + if t.TopRow > len(t.Rows)-(t.Y-1) { + t.TopRow = len(t.Rows) - (t.Y - 1) } - // calculate gap size based on total width - gap := 3 - if t.X < 50 { - gap = 1 - } else if t.X < 75 { - gap = 2 - } - - cw := []int{5, 10, 4, 4} // cellWidth - cp := []int{ // cellPosition - gap, - gap + cw[0] + gap, - t.X - gap - cw[3] - gap - cw[2], - t.X - gap - cw[3], - } - - // total width requires by all 4 columns - contentWidth := gap + cw[0] + gap + cw[1] + gap + cw[2] + gap + cw[3] + gap - render := 4 // number of columns to render based on the terminal width - - // removes CPU and MEM columns if there isn't enough room - if t.X < (contentWidth - gap - cw[3]) { - render = 2 - } else if t.X < contentWidth { - cp[2] = cp[3] - render = 3 - } + t.ColResizer() // print header - for i := 0; i < render; i++ { + // for i := 0; i < render; i++ { + for i, width := range t.ColWidths { + if width == 0 { + break + } r := MaxString(t.Header[i], t.X-6) - buf.SetString(cp[i], 1, r, t.Fg|AttrBold, t.Bg) + buf.SetString(t.Cp[i], 1, r, t.Fg|AttrBold, t.Bg) } // prints each row - for rowNum := t.topRow; rowNum < t.topRow+t.Y-1 && rowNum < len(t.Rows); rowNum++ { + for rowNum := t.TopRow; rowNum < t.TopRow+t.Y-1 && rowNum < len(t.Rows); rowNum++ { row := t.Rows[rowNum] - y := (rowNum + 2) - t.topRow + y := (rowNum + 2) - t.TopRow // cursor bg := t.Bg - if (t.pid == "" && rowNum == t.selected) || (t.pid != "" && t.pid == row[t.UniqueCol]) { + if (t.SelectedItem == "" && rowNum == t.SelectedRow) || (t.SelectedItem != "" && t.SelectedItem == row[t.UniqueCol]) { bg = t.Cursor - for i := 0; i < render; i++ { + for _, width := range t.ColWidths { + if width == 0 { + break + } buf.SetString(1, y, strings.Repeat(" ", t.X), t.Fg, bg) } - t.pid = row[t.UniqueCol] - t.selected = rowNum + t.SelectedItem = row[t.UniqueCol] + t.SelectedRow = rowNum } // prints each col of the row - for i := 0; i < render; i++ { + for i, width := range t.ColWidths { + if width == 0 { + break + } r := MaxString(row[i], t.X-6) - buf.SetString(cp[i], y, r, t.Fg, bg) + buf.SetString(t.Cp[i], y, r, t.Fg, bg) } } @@ -103,63 +105,64 @@ func (t *Table) Buffer() *Buffer { } //////////////////////////////////////////////////////////////////////////////// +// Cursor movement // calcPos is used to calculate the cursor position and where in the process list we are located. func (t *Table) calcPos() { - t.pid = "" + t.SelectedItem = "" - if t.selected < 0 { - t.selected = 0 + if t.SelectedRow < 0 { + t.SelectedRow = 0 } - if t.selected < t.topRow { - t.topRow = t.selected + if t.SelectedRow < t.TopRow { + t.TopRow = t.SelectedRow } - if t.selected > len(t.Rows)-1 { - t.selected = len(t.Rows) - 1 + if t.SelectedRow > len(t.Rows)-1 { + t.SelectedRow = len(t.Rows) - 1 } - if t.selected > t.topRow+(t.Y-2) { - t.topRow = t.selected - (t.Y - 2) + if t.SelectedRow > t.TopRow+(t.Y-2) { + t.TopRow = t.SelectedRow - (t.Y - 2) } } func (t *Table) Up() { - t.selected -= 1 + t.SelectedRow -= 1 t.calcPos() } func (t *Table) Down() { - t.selected += 1 + t.SelectedRow += 1 t.calcPos() } func (t *Table) Top() { - t.selected = 0 + t.SelectedRow = 0 t.calcPos() } func (t *Table) Bottom() { - t.selected = len(t.Rows) - 1 + t.SelectedRow = len(t.Rows) - 1 t.calcPos() } func (t *Table) HalfPageUp() { - t.selected = t.selected - (t.Y-2)/2 + t.SelectedRow = t.SelectedRow - (t.Y-2)/2 t.calcPos() } func (t *Table) HalfPageDown() { - t.selected = t.selected + (t.Y-2)/2 + t.SelectedRow = t.SelectedRow + (t.Y-2)/2 t.calcPos() } func (t *Table) PageUp() { - t.selected -= (t.Y - 2) + t.SelectedRow -= (t.Y - 2) t.calcPos() } func (t *Table) PageDown() { - t.selected += (t.Y - 2) + t.SelectedRow += (t.Y - 2) t.calcPos() } @@ -167,18 +170,7 @@ func (t *Table) Click(x, y int) { x = x - t.XOffset y = y - t.YOffset if (x > 0 && x <= t.X) && (y > 0 && y <= t.Y) { - t.selected = (t.topRow + y) - 2 + t.SelectedRow = (t.TopRow + y) - 2 t.calcPos() } } - -// Kill kills process or group of processes. -func (t *Table) Kill() { - t.pid = "" - command := "kill" - if t.UniqueCol == 1 { - command = "pkill" - } - cmd := exec.Command(command, t.Rows[t.selected][t.UniqueCol]) - cmd.Start() -} diff --git a/widgets/proc.go b/widgets/proc.go index 248fb7c..f08367e 100644 --- a/widgets/proc.go +++ b/widgets/proc.go @@ -1,6 +1,7 @@ package widgets import ( + "os/exec" "sort" "strconv" "time" @@ -46,7 +47,9 @@ func NewProc(loaded, keyPressed chan bool) *Proc { group: true, KeyPressed: keyPressed, } + p.ColResizer = p.ColResize p.Label = "Process List" + p.ColWidths = []int{5, 10, 4, 4} p.UniqueCol = 0 if p.group { @@ -126,6 +129,35 @@ func (p *Proc) Sort() { p.Rows = FieldsToStrings(*processes) } +// ColResize overrides the default ColResize in the termui table. +func (p *Proc) ColResize() { + // calculate gap size based on total width + p.Gap = 3 + if p.X < 50 { + p.Gap = 1 + } else if p.X < 75 { + p.Gap = 2 + } + + p.Cp = []int{ + p.Gap, + p.Gap + p.ColWidths[0] + p.Gap, + p.X - p.Gap - p.ColWidths[3] - p.Gap - p.ColWidths[2], + p.X - p.Gap - p.ColWidths[3], + } + + contentWidth := p.Gap + p.ColWidths[0] + p.Gap + p.ColWidths[1] + p.Gap + p.ColWidths[2] + p.Gap + p.ColWidths[3] + p.Gap + + // only renders a column if it fits + if p.X < (contentWidth - p.Gap - p.ColWidths[3]) { + p.ColWidths[2] = 0 + p.ColWidths[3] = 0 + } else if p.X < contentWidth { + p.Cp[2] = p.Cp[3] + p.ColWidths[3] = 0 + } +} + func (p *Proc) keyBinds() { ui.On("MouseLeft", func(e ui.Event) { p.Click(e.MouseX, e.MouseY) @@ -249,6 +281,17 @@ func FieldsToStrings(P []Process) [][]string { return strings } +// Kill kills process or group of processes. +func (p *Proc) Kill() { + p.SelectedItem = "" + command := "kill" + if p.UniqueCol == 1 { + command = "pkill" + } + cmd := exec.Command(command, p.Rows[p.SelectedRow][p.UniqueCol]) + cmd.Start() +} + //////////////////////////////////////////////////////////////////////////////// // Sorting diff --git a/widgets/temp.go b/widgets/temp.go index e965629..d5bf520 100644 --- a/widgets/temp.go +++ b/widgets/temp.go @@ -1,6 +1,10 @@ package widgets +// Temp is too customized to inherit from a generic widget so we create a customized one here. +// Temp defines its own Buffer method directly. + import ( + "fmt" "strings" "time" @@ -9,14 +13,22 @@ import ( ) type Temp struct { - *ui.List - interval time.Duration + *ui.Block + interval time.Duration + Data []int + DataLabels []string + Threshold int + TempLow ui.Color + TempHigh ui.Color } func NewTemp() *Temp { - t := &Temp{ui.NewList(), time.Second * 5} + t := &Temp{ + Block: ui.NewBlock(), + interval: time.Second * 5, + Threshold: 80, // temp at which color should change to red + } t.Label = "Temperatures" - t.Threshold = 80 // temp at which color should change to red go t.update() ticker := time.NewTicker(t.interval) @@ -44,3 +56,25 @@ func (t *Temp) update() { t.Data = temps t.DataLabels = labels } + +// Buffer implements ui.Bufferer interface. +func (t *Temp) Buffer() *ui.Buffer { + buf := t.Block.Buffer() + + for y, text := range t.DataLabels { + if y+1 > t.Y { + break + } + + fg := t.TempLow + if t.Data[y] >= t.Threshold { + fg = t.TempHigh + } + + s := ui.MaxString(text, (t.X - 4)) + buf.SetString(1, y+1, s, t.Fg, t.Bg) + buf.SetString(t.X-2, y+1, fmt.Sprintf("%dC", t.Data[y]), fg, t.Bg) + } + + return buf +}