Factor Entry widget out of proc.

This commit is contained in:
Brian Mattern 2019-06-04 15:08:02 -07:00
parent 84a4bbd440
commit 9c0253ef31
2 changed files with 150 additions and 85 deletions

132
src/termui/entry.go Normal file
View File

@ -0,0 +1,132 @@
package termui
import (
"image"
"strings"
"unicode/utf8"
"github.com/cjbassi/gotop/src/utils"
. "github.com/gizak/termui/v3"
rw "github.com/mattn/go-runewidth"
)
const (
ELLIPSIS = "…"
CURSOR = " "
)
type Entry struct {
Block
Style Style
Label string
Value string
ShowWhenEmpty bool
UpdateCallback func(string)
editing bool
}
func (self *Entry) SetEditing(editing bool) {
self.editing = editing
}
func (self *Entry) update() {
if self.UpdateCallback != nil {
self.UpdateCallback(self.Value)
}
}
// HandleEvent handles input events if the entry is being edited.
// Returns true if the event was handled.
func (self *Entry) HandleEvent(e Event) bool {
if !self.editing {
return false
}
if utf8.RuneCountInString(e.ID) == 1 {
self.Value += e.ID
self.update()
return true
}
switch e.ID {
case "<C-c>", "<Escape>":
self.Value = ""
self.editing = false
self.update()
case "<Enter>":
self.editing = false
case "<Backspace>":
if self.Value != "" {
r := []rune(self.Value)
self.Value = string(r[:len(r)-1])
self.update()
}
case "<Space>":
self.Value += " "
self.update()
default:
return false
}
return true
}
func TruncateFront(s string, w int, prefix string) string {
if rw.StringWidth(s) < w {
return s
}
r := []rune(s)
pw := rw.StringWidth(prefix)
w -= pw
width := 0
i := len(r) - 1
for ; i >= 0 && width <= w; i-- {
cw := rw.RuneWidth(r[i])
width += cw
if width > w {
break
}
}
return prefix + string(r[i+1:len(r)])
}
func (self *Entry) Draw(buf *Buffer) {
if self.Value == "" && !self.editing && !self.ShowWhenEmpty {
return
}
style := self.Style
label := self.Label
if self.editing {
label += "["
style = NewStyle(style.Fg, style.Bg, ModifierBold)
}
cursorStyle := NewStyle(style.Bg, style.Fg, ModifierClear)
p := image.Pt(self.Min.X, self.Min.Y)
buf.SetString(label, style, p)
p.X += rw.StringWidth(label)
tail := " "
if self.editing {
tail = "] "
}
maxLen := self.Max.X - p.X - rw.StringWidth(tail)
if self.editing {
maxLen -= 1 // for cursor
}
value := utils.TruncateFront(self.Value, maxLen, ELLIPSIS)
buf.SetString(value, self.Style, p)
p.X += rw.StringWidth(value)
if self.editing {
buf.SetString(CURSOR, cursorStyle, p)
p.X += rw.StringWidth(CURSOR)
if remaining := maxLen - rw.StringWidth(value); remaining > 0 {
buf.SetString(strings.Repeat(" ", remaining), self.TitleStyle, p)
p.X += remaining
}
}
buf.SetString(tail, style, p)
}

View File

@ -2,28 +2,23 @@ package widgets
import (
"fmt"
"image"
"log"
"os/exec"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
psCPU "github.com/shirou/gopsutil/cpu"
ui "github.com/cjbassi/gotop/src/termui"
"github.com/cjbassi/gotop/src/utils"
tui "github.com/gizak/termui/v3"
rw "github.com/mattn/go-runewidth"
)
const (
UP_ARROW = "▲"
DOWN_ARROW = "▼"
ELLIPSIS = "…"
CURSOR = " "
)
type ProcSortMethod string
@ -44,11 +39,11 @@ type Proc struct {
type ProcWidget struct {
*ui.Table
entry *ui.Entry
cpuCount int
updateInterval time.Duration
sortMethod ProcSortMethod
filter string
editingFilter bool
groupedProcs []Proc
ungroupedProcs []Proc
showGroupedProcs bool
@ -66,7 +61,15 @@ func NewProcWidget() *ProcWidget {
sortMethod: ProcSortCpu,
showGroupedProcs: true,
filter: "",
editingFilter: false,
}
self.entry = &ui.Entry{
Style: self.TitleStyle,
Label: " Filter: ",
Value: "foobar",
UpdateCallback: func(val string) {
self.filter = val
self.update()
},
}
self.Title = " Processes "
self.ShowCursor = true
@ -98,91 +101,21 @@ func NewProcWidget() *ProcWidget {
}
func (self *ProcWidget) SetEditingFilter(editing bool) {
self.editingFilter = editing
if !editing {
self.update()
}
self.entry.SetEditing(editing)
}
// handleEditFilterEvents handles events while editing the proc filter.
// Returns true if the event was handled.
func (self *ProcWidget) HandleEvent(e tui.Event) bool {
if !self.editingFilter {
return false
}
if utf8.RuneCountInString(e.ID) == 1 {
self.filter += e.ID
self.update()
return true
}
switch e.ID {
case "<C-c>", "<Escape>":
self.filter = ""
self.update()
self.SetEditingFilter(false)
case "<Enter>":
self.SetEditingFilter(false)
case "<Backspace>":
if self.filter != "" {
r := []rune(self.filter)
self.filter = string(r[:len(r)-1])
self.update()
}
case "<Space>":
self.filter += " "
self.update()
default:
return false
}
return true
return self.entry.HandleEvent(e)
}
func (self *ProcWidget) SetRect(x1, y1, x2, y2 int) {
self.Table.SetRect(x1, y1, x2, y2)
self.entry.SetRect(x1+2, y2-1, x2-2, y2)
}
func (self *ProcWidget) Draw(buf *tui.Buffer) {
self.Table.Draw(buf)
if self.filter != "" || self.editingFilter {
self.drawFilter(buf)
}
}
func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
padding := 2
style := self.TitleStyle
label := " Filter: "
if self.editingFilter {
label += "["
style = tui.NewStyle(style.Fg, style.Bg, tui.ModifierBold)
}
cursorStyle := tui.NewStyle(style.Bg, style.Fg, tui.ModifierClear)
p := image.Pt(self.Min.X+padding, self.Max.Y-1)
buf.SetString(label, style, p)
p.X += rw.StringWidth(label)
tail := " "
if self.editingFilter {
tail = "] "
}
maxLen := self.Max.X - p.X - padding - rw.StringWidth(tail)
if self.editingFilter {
maxLen -= 1 // for cursor
}
filter := utils.TruncateFront(self.filter, maxLen, ELLIPSIS)
buf.SetString(filter, self.TitleStyle, p)
p.X += rw.StringWidth(filter)
if self.editingFilter {
buf.SetString(CURSOR, cursorStyle, p)
p.X += rw.StringWidth(CURSOR)
if remaining := maxLen - rw.StringWidth(filter); remaining > 0 {
buf.SetString(strings.Repeat(" ", remaining), self.TitleStyle, p)
p.X += remaining
}
}
buf.SetString(tail, style, p)
self.entry.Draw(buf)
}
func (self *ProcWidget) filterProcs(procs []Proc) []Proc {