From 9c0253ef3193d8259ba12cc51b3c2b694f309485 Mon Sep 17 00:00:00 2001 From: Brian Mattern Date: Tue, 4 Jun 2019 15:08:02 -0700 Subject: [PATCH] Factor Entry widget out of proc. --- src/termui/entry.go | 132 ++++++++++++++++++++++++++++++++++++++++++++ src/widgets/proc.go | 103 ++++++---------------------------- 2 files changed, 150 insertions(+), 85 deletions(-) create mode 100644 src/termui/entry.go diff --git a/src/termui/entry.go b/src/termui/entry.go new file mode 100644 index 0000000..e0930f7 --- /dev/null +++ b/src/termui/entry.go @@ -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 "", "": + self.Value = "" + self.editing = false + self.update() + case "": + self.editing = false + case "": + if self.Value != "" { + r := []rune(self.Value) + self.Value = string(r[:len(r)-1]) + self.update() + } + case "": + 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) +} diff --git a/src/widgets/proc.go b/src/widgets/proc.go index a598498..106d970 100644 --- a/src/widgets/proc.go +++ b/src/widgets/proc.go @@ -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 "", "": - self.filter = "" - self.update() - self.SetEditingFilter(false) - case "": - self.SetEditingFilter(false) - case "": - if self.filter != "" { - r := []rune(self.filter) - self.filter = string(r[:len(r)-1]) - self.update() - } - case "": - 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 {