2019-01-01 07:01:13 +08:00
|
|
|
package termui
|
|
|
|
|
|
|
|
import (
|
2019-01-01 08:55:50 +08:00
|
|
|
"image"
|
|
|
|
|
|
|
|
. "github.com/gizak/termui"
|
2019-01-01 07:01:13 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers.
|
|
|
|
type Sparkline struct {
|
|
|
|
Data []int
|
|
|
|
Title1 string
|
|
|
|
Title2 string
|
2019-01-24 13:23:35 +08:00
|
|
|
TitleColor Color
|
|
|
|
LineColor Color
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sparklines is a renderable widget which groups together the given sparklines.
|
|
|
|
type Sparklines struct {
|
|
|
|
*Block
|
|
|
|
Lines []*Sparkline
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add appends a given Sparkline to the *Sparklines.
|
|
|
|
func (self *Sparklines) Add(sl Sparkline) {
|
|
|
|
self.Lines = append(self.Lines, &sl)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewSparkline returns an unrenderable single sparkline that intended to be added into a Sparklines.
|
|
|
|
func NewSparkline() *Sparkline {
|
2019-01-01 08:55:50 +08:00
|
|
|
return &Sparkline{}
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewSparklines return a new *Sparklines with given Sparklines, you can always add a new Sparkline later.
|
|
|
|
func NewSparklines(ss ...*Sparkline) *Sparklines {
|
|
|
|
return &Sparklines{
|
|
|
|
Block: NewBlock(),
|
|
|
|
Lines: ss,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-01 08:55:50 +08:00
|
|
|
func (self *Sparklines) Draw(buf *Buffer) {
|
|
|
|
self.Block.Draw(buf)
|
2019-01-01 07:01:13 +08:00
|
|
|
|
|
|
|
lc := len(self.Lines) // lineCount
|
|
|
|
|
|
|
|
// renders each sparkline and its titles
|
|
|
|
for i, line := range self.Lines {
|
|
|
|
|
|
|
|
// prints titles
|
2019-01-01 18:29:14 +08:00
|
|
|
title1Y := self.Inner.Min.Y + 1 + (self.Inner.Dy()/lc)*i
|
|
|
|
title2Y := self.Inner.Min.Y + 2 + (self.Inner.Dy()/lc)*i
|
2019-01-01 08:55:50 +08:00
|
|
|
title1 := TrimString(line.Title1, self.Inner.Dx())
|
|
|
|
title2 := TrimString(line.Title2, self.Inner.Dx())
|
2019-01-01 18:29:14 +08:00
|
|
|
if self.Inner.Dy() > 5 {
|
|
|
|
buf.SetString(
|
|
|
|
title1,
|
2019-01-24 13:23:35 +08:00
|
|
|
NewStyle(line.TitleColor, ColorClear, ModifierBold),
|
2019-01-15 11:47:44 +08:00
|
|
|
image.Pt(self.Inner.Min.X, title1Y),
|
2019-01-01 18:29:14 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
if self.Inner.Dy() > 6 {
|
|
|
|
buf.SetString(
|
|
|
|
title2,
|
2019-01-24 13:23:35 +08:00
|
|
|
NewStyle(line.TitleColor, ColorClear, ModifierBold),
|
2019-01-15 11:47:44 +08:00
|
|
|
image.Pt(self.Inner.Min.X, title2Y),
|
2019-01-01 18:29:14 +08:00
|
|
|
)
|
|
|
|
}
|
2019-01-01 07:01:13 +08:00
|
|
|
|
2019-01-01 08:55:50 +08:00
|
|
|
sparkY := (self.Inner.Dy() / lc) * (i + 1)
|
2019-01-01 07:01:13 +08:00
|
|
|
// finds max data in current view used for relative heights
|
|
|
|
max := 1
|
2019-01-01 08:55:50 +08:00
|
|
|
for i := len(line.Data) - 1; i >= 0 && self.Inner.Dx()-((len(line.Data)-1)-i) >= 1; i-- {
|
2019-01-01 07:01:13 +08:00
|
|
|
if line.Data[i] > max {
|
|
|
|
max = line.Data[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// prints sparkline
|
2019-01-01 08:55:50 +08:00
|
|
|
for x := self.Inner.Dx(); x >= 1; x-- {
|
2019-01-24 13:23:35 +08:00
|
|
|
char := BARS[0]
|
2019-01-01 08:55:50 +08:00
|
|
|
if (self.Inner.Dx() - x) < len(line.Data) {
|
|
|
|
offset := self.Inner.Dx() - x
|
2019-01-24 13:23:35 +08:00
|
|
|
curItem := line.Data[(len(line.Data)-1)-offset]
|
|
|
|
percent := float64(curItem) / float64(max)
|
2019-01-01 07:01:13 +08:00
|
|
|
index := int(percent * 7)
|
2019-01-24 13:23:35 +08:00
|
|
|
if index < 0 || index >= len(BARS) {
|
2019-01-01 08:55:50 +08:00
|
|
|
panic("TODO")
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
2019-01-24 13:23:35 +08:00
|
|
|
char = BARS[index]
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
2019-01-01 08:55:50 +08:00
|
|
|
buf.SetCell(
|
2019-01-24 13:23:35 +08:00
|
|
|
Cell{char, NewStyle(line.LineColor)},
|
2019-01-01 08:55:50 +08:00
|
|
|
image.Pt(self.Inner.Min.X+x-1, self.Inner.Min.Y+sparkY-1),
|
|
|
|
)
|
2019-01-01 07:01:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|