ctop/widgets/view.go

126 lines
2.7 KiB
Go
Raw Normal View History

2017-11-25 19:30:50 +01:00
package widgets
import (
2017-11-28 14:40:43 +01:00
ui "github.com/gizak/termui"
"github.com/mattn/go-runewidth"
2017-11-25 19:30:50 +01:00
)
type ToggleText interface {
// returns text for toggle on/off
Toggle(on bool) string
}
2017-11-25 19:30:50 +01:00
type TextView struct {
ui.Block
inputStream <-chan ToggleText
2017-11-25 19:30:50 +01:00
render chan bool
toggleState bool
Text []ToggleText // all the text
TextOut []string // text to be displayed
2017-11-25 19:30:50 +01:00
TextFgColor ui.Attribute
TextBgColor ui.Attribute
padding Padding
}
func NewTextView(lines <-chan ToggleText) *TextView {
2017-12-01 16:29:11 +01:00
t := &TextView{
2017-11-25 19:30:50 +01:00
Block: *ui.NewBlock(),
inputStream: lines,
render: make(chan bool),
Text: []ToggleText{},
2017-11-25 19:30:50 +01:00
TextOut: []string{},
TextFgColor: ui.ThemeAttr("menu.text.fg"),
TextBgColor: ui.ThemeAttr("menu.text.bg"),
padding: Padding{4, 2},
}
2017-12-01 16:29:11 +01:00
t.BorderFg = ui.ThemeAttr("menu.border.fg")
t.BorderLabelFg = ui.ThemeAttr("menu.label.fg")
t.Height = ui.TermHeight()
t.Width = ui.TermWidth()
2017-11-25 19:30:50 +01:00
2017-12-01 16:29:11 +01:00
t.readInputLoop()
t.renderLoop()
return t
2017-11-25 19:30:50 +01:00
}
// Adjusts text inside this view according to the window size. No need to call ui.Render(...)
// after calling this method, it is called automatically
2017-12-01 16:29:11 +01:00
func (t *TextView) Resize() {
2017-11-28 14:55:29 +01:00
ui.Clear()
2017-12-01 16:29:11 +01:00
t.Height = ui.TermHeight()
t.Width = ui.TermWidth()
t.render <- true
2017-11-28 14:55:29 +01:00
}
// Toggles text inside this view. No need to call ui.Render(...) after calling this method,
// it is called automatically
func (t *TextView) Toggle() {
t.toggleState = !t.toggleState
t.render <- true
}
2017-12-01 16:29:11 +01:00
func (t *TextView) Buffer() ui.Buffer {
2017-11-25 19:30:50 +01:00
var cell ui.Cell
2017-12-01 16:29:11 +01:00
buf := t.Block.Buffer()
2017-11-25 19:30:50 +01:00
2017-12-01 16:29:11 +01:00
x := t.Block.X + t.padding[0]
y := t.Block.Y + t.padding[1]
2017-11-25 19:30:50 +01:00
2017-12-01 16:29:11 +01:00
for _, line := range t.TextOut {
2017-11-25 19:30:50 +01:00
for _, ch := range line {
2017-12-01 16:29:11 +01:00
cell = ui.Cell{Ch: ch, Fg: t.TextFgColor, Bg: t.TextBgColor}
2017-11-25 19:30:50 +01:00
buf.Set(x, y, cell)
x = x + runewidth.RuneWidth(ch)
2017-11-25 19:30:50 +01:00
}
2017-12-01 16:29:11 +01:00
x = t.Block.X + t.padding[0]
2017-11-25 19:30:50 +01:00
y++
}
return buf
}
2017-12-01 16:29:11 +01:00
func (t *TextView) renderLoop() {
2017-11-25 19:30:50 +01:00
go func() {
2017-12-01 16:29:11 +01:00
for range t.render {
maxWidth := t.Width - (t.padding[0] * 2)
height := t.Height - (t.padding[1] * 2)
t.TextOut = []string{}
for i := len(t.Text) - 1; i >= 0; i-- {
lines := splitLine(t.Text[i].Toggle(t.toggleState), maxWidth)
2017-12-01 16:29:11 +01:00
t.TextOut = append(lines, t.TextOut...)
if len(t.TextOut) > height {
t.TextOut = t.TextOut[:height]
break
}
2017-11-25 19:30:50 +01:00
}
2017-12-01 16:29:11 +01:00
ui.Render(t)
2017-11-25 19:30:50 +01:00
}
}()
}
2017-12-01 16:29:11 +01:00
func (t *TextView) readInputLoop() {
2017-11-25 19:30:50 +01:00
go func() {
2017-12-01 16:29:11 +01:00
for line := range t.inputStream {
t.Text = append(t.Text, line)
t.render <- true
2017-11-25 19:30:50 +01:00
}
2017-12-01 16:29:11 +01:00
close(t.render)
2017-11-25 19:30:50 +01:00
}()
}
2017-12-01 16:29:11 +01:00
func splitLine(line string, lineSize int) []string {
2017-12-01 16:51:55 +01:00
if line == "" {
return []string{}
}
2017-12-01 16:29:11 +01:00
var lines []string
for {
2017-12-01 16:51:55 +01:00
if len(line) <= lineSize {
2017-12-01 16:29:11 +01:00
lines = append(lines, line)
return lines
}
lines = append(lines, line[:lineSize])
line = line[lineSize:]
}
}