Factor Entry widget out of proc.
This commit is contained in:
parent
84a4bbd440
commit
9c0253ef31
132
src/termui/entry.go
Normal file
132
src/termui/entry.go
Normal 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)
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user