Removed gotop specific stuff from termui
This commit is contained in:
parent
a2fd8dc4fa
commit
21aed4b1ea
|
@ -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
|
||||
|
|
7
gotop.go
7
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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -8,7 +8,6 @@ type Sparkline struct {
|
|||
Title1 string
|
||||
Title2 string
|
||||
TitleColor Color
|
||||
Total int
|
||||
LineColor Color
|
||||
}
|
||||
|
||||
|
|
168
termui/table.go
168
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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user