Removed gotop specific stuff from termui

This commit is contained in:
Caleb Bassi 2018-02-24 21:35:57 -08:00
parent a2fd8dc4fa
commit 21aed4b1ea
9 changed files with 174 additions and 157 deletions

View File

@ -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

View File

@ -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() {

View File

@ -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
}

View File

@ -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})

View File

@ -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
}

View File

@ -8,7 +8,6 @@ type Sparkline struct {
Title1 string
Title2 string
TitleColor Color
Total int
LineColor Color
}

View File

@ -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()
}

View File

@ -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

View File

@ -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
}