reworked pulses view

mine
derailed 2020-03-02 23:28:07 -07:00
parent 6bc84c2304
commit 3212a751ea
10 changed files with 194 additions and 75 deletions

View File

@ -79,7 +79,7 @@ linters-settings:
# exclude: /path/to/file.txt
funlen:
lines: 60
lines: 65
statements: 40
govet:

View File

@ -21,12 +21,12 @@ func infoCmd() *cobra.Command {
}
func printInfo() {
const sectionFmt = "%-15s "
const fmat = "%-25s %s\n"
printLogo(color.Cyan)
printTuple(sectionFmt, "Configuration", config.K9sConfigFile, color.Cyan)
printTuple(sectionFmt, "Logs", config.K9sLogs, color.Cyan)
printTuple(sectionFmt, "Screen Dumps", config.K9sDumpDir, color.Cyan)
printTuple(fmat, "Configuration", config.K9sConfigFile, color.Cyan)
printTuple(fmat, "Logs", config.K9sLogs, color.Cyan)
printTuple(fmat, "Screen Dumps", config.K9sDumpDir, color.Cyan)
}
func printLogo(c color.Paint) {

View File

@ -25,7 +25,7 @@ func versionCmd() *cobra.Command {
}
func printVersion(short bool) {
const secFmt = "%-10s "
const fmat = "%-20s %s\n"
var outputColor color.Paint
if short {
@ -34,15 +34,15 @@ func printVersion(short bool) {
outputColor = color.Cyan
printLogo(outputColor)
}
printTuple(secFmt, "Version", version, outputColor)
printTuple(secFmt, "Commit", commit, outputColor)
printTuple(secFmt, "Date", date, outputColor)
printTuple(fmat, "Version", version, outputColor)
printTuple(fmat, "Commit", commit, outputColor)
printTuple(fmat, "Date", date, outputColor)
}
func printTuple(format, section, value string, outputColor color.Paint) {
func printTuple(fmat, section, value string, outputColor color.Paint) {
if outputColor != -1 {
section = color.Colorize(fmt.Sprintf(section+":"), outputColor)
value = color.Colorize(value, color.White)
fmt.Printf(fmat, color.Colorize(section+":", outputColor), color.Colorize(value, color.White))
return
}
fmt.Println(fmt.Sprintf(format, section), value)
fmt.Printf(fmat, section, value)
}

View File

@ -17,12 +17,13 @@ const (
type Component struct {
*tview.Box
bgColor, noColor tcell.Color
seriesColors []tcell.Color
dimmed tcell.Style
id, legend string
blur func(tcell.Key)
mx sync.RWMutex
bgColor, noColor tcell.Color
focusFgColor, focusBgColor string
seriesColors []tcell.Color
dimmed tcell.Style
id, legend string
blur func(tcell.Key)
mx sync.RWMutex
}
// NewComponent returns a new component.
@ -36,6 +37,11 @@ func NewComponent(id string) *Component {
}
}
// SetFocusColorNames sets the focus color names.
func (c *Component) SetFocusColorNames(fg, bg string) {
c.focusFgColor, c.focusBgColor = fg, bg
}
// SetBackgroundColor sets the graph bg color.
func (c *Component) SetBackgroundColor(color tcell.Color) {
c.Box.SetBackgroundColor(color)

View File

@ -2,10 +2,28 @@ package tchart
import (
"fmt"
"github.com/derailed/tview"
)
var dots = []rune{' ', '⠂', '▤', '▥'}
const (
b = ' '
h = tview.BoxDrawingsHeavyHorizontal
v = tview.BoxDrawingsHeavyVertical
tl = tview.BoxDrawingsHeavyDownAndRight
tr = tview.BoxDrawingsHeavyDownAndLeft
bl = tview.BoxDrawingsHeavyUpAndRight
br = tview.BoxDrawingsHeavyUpAndLeft
teeL = tview.BoxDrawingsHeavyVerticalAndLeft
teeR = tview.BoxDrawingsHeavyVerticalAndRight
lh = '\u2578'
rh = '\u257a'
hv = '\u2579'
lv = '\u257b'
)
// Segment represents a dial segment.
type Segment []int
@ -33,6 +51,9 @@ func NewDotMatrix(row, col int) DotMatrix {
// Print prints the matrix.
func (d DotMatrix) Print(n int) Matrix {
if d.row == d.col {
return To3x3Char(n)
}
m := make(Matrix, d.row)
segs := asSegments(n)
for row := 0; row < d.row; row++ {
@ -71,9 +92,9 @@ func asSegments(n int) Segment {
}
}
// CharFor return a char based on row/col.
// CharFor returns a char based on row/col.
func (s Segment) CharFor(row, col int) rune {
c := ' '
c := dots[0]
segs := ToSegments(row, col)
if segs == nil {
return c
@ -110,3 +131,69 @@ var segs = map[int][][]int{
func ToSegments(row, col int) []int {
return segs[row][col]
}
// To3x3Char returns 3x3 number matrix
func To3x3Char(numb int) Matrix {
switch numb {
case 1:
return Matrix{
[]rune{b, lv, b},
[]rune{b, v, b},
[]rune{b, hv, b},
}
case 2:
return Matrix{
[]rune{rh, h, tr},
[]rune{tl, h, br},
[]rune{bl, h, lh},
}
case 3:
return Matrix{
[]rune{h, h, tr},
[]rune{rh, h, teeL},
[]rune{h, h, br},
}
case 4:
return Matrix{
[]rune{lv, b, lv},
[]rune{bl, h, teeL},
[]rune{b, b, hv},
}
case 5:
return Matrix{
[]rune{tl, h, lh},
[]rune{bl, h, tr},
[]rune{rh, h, br},
}
case 6:
return Matrix{
[]rune{tl, h, lh},
[]rune{teeR, h, tr},
[]rune{bl, h, br},
}
case 7:
return Matrix{
[]rune{h, h, tr},
[]rune{b, b, v},
[]rune{b, b, hv},
}
case 8:
return Matrix{
[]rune{tl, h, tr},
[]rune{teeR, h, teeL},
[]rune{bl, h, br},
}
case 9:
return Matrix{
[]rune{tl, h, tr},
[]rune{bl, h, teeL},
[]rune{rh, h, br},
}
default:
return Matrix{
[]rune{tl, h, tr},
[]rune{v, b, v},
[]rune{bl, h, br},
}
}
}

View File

@ -1,6 +1,7 @@
package tchart_test
import (
"fmt"
"strconv"
"testing"
@ -48,6 +49,17 @@ func TestDial5x3(t *testing.T) {
}
}
func TestDial3x3(t *testing.T) {
d := tchart.NewDotMatrix(3, 3)
for n := 0; n <= 2; n++ {
i := n
t.Run(strconv.Itoa(n), func(t *testing.T) {
fmt.Println(tchart.To3x3Char(i))
assert.Equal(t, tchart.To3x3Char(i), d.Print(i))
})
}
}
// Helpers...
const hChar, vChar = '▤', '▥'

View File

@ -28,6 +28,7 @@ type Gauge struct {
*Component
data Metric
resolution int
deltaOk, deltaFault delta
}
@ -38,6 +39,10 @@ func NewGauge(id string) *Gauge {
}
}
func (g *Gauge) SetResolution(n int) {
g.resolution = n
}
// IsDial returns true if chart is a dial
func (g *Gauge) IsDial() bool {
return true
@ -60,32 +65,60 @@ func (g *Gauge) Draw(sc tcell.Screen) {
defer g.mx.RUnlock()
rect := g.asRect()
mid := image.Point{X: rect.Min.X + rect.Dx()/2 - 1, Y: rect.Min.Y + rect.Dy()/2 - 2}
mid := image.Point{X: rect.Min.X + rect.Dx()/2, Y: rect.Min.Y + rect.Dy()/2 - 1}
style := tcell.StyleDefault.Background(g.bgColor)
style = style.Foreground(tcell.ColorYellow)
sc.SetContent(mid.X+1, mid.Y+2, '⠔', nil, style)
sc.SetContent(mid.X, mid.Y, '⠔', nil, style)
max := g.data.MaxDigits()
if max < g.resolution {
max = g.resolution
}
var (
max = g.data.MaxDigits()
fmat = "%" + fmt.Sprintf(gaugeFmt, max)
o = image.Point{X: mid.X - 3, Y: mid.Y}
o = image.Point{X: mid.X, Y: mid.Y - 1}
)
s1C, s2C := g.colorForSeries()
d1, d2 := fmt.Sprintf(fmat, g.data.OK), fmt.Sprintf(fmat, g.data.Fault)
o.X -= (len(d1) - 1) * 5
o.X -= len(d1) * 3
g.drawNum(sc, true, o, g.data.OK, g.deltaOk, d1, style.Foreground(s1C).Dim(false))
o.X = mid.X + 3
o.X = mid.X + 1
g.drawNum(sc, false, o, g.data.Fault, g.deltaFault, d2, style.Foreground(s2C).Dim(false))
if rect.Dx() > 0 && rect.Dy() > 0 && g.legend != "" {
legend := g.legend
if g.HasFocus() {
legend = "[:aqua]" + g.legend + "[::]"
legend = fmt.Sprintf("[%s:%s:]", g.focusFgColor, g.focusBgColor) + g.legend + "[::]"
}
tview.Print(sc, legend, rect.Min.X, rect.Max.Y, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
tview.Print(sc, legend, rect.Min.X, o.Y+3, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
}
}
func (g *Gauge) drawNum(sc tcell.Screen, ok bool, o image.Point, n int, dn delta, ns string, style tcell.Style) {
c1, _ := g.colorForSeries()
if ok {
style = style.Foreground(c1)
printDelta(sc, dn, o, style)
}
dm, significant := NewDotMatrix(3, 3), n == 0
if n == 0 {
style = g.dimmed
}
for i := 0; i < len(ns); i++ {
if ns[i] == '0' && !significant {
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, g.dimmed)
} else {
significant = true
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, style)
}
o.X += 3
}
if !ok {
o.X++
printDelta(sc, dn, o, style)
}
}
@ -102,33 +135,6 @@ func (g *Gauge) drawDial(sc tcell.Screen, m Matrix, o image.Point, style tcell.S
}
}
func (g *Gauge) drawNum(sc tcell.Screen, ok bool, o image.Point, n int, dn delta, ns string, style tcell.Style) {
c1, _ := g.colorForSeries()
if ok {
o.X -= 1
style = style.Foreground(c1)
printDelta(sc, dn, o, style)
o.X += 1
}
dm, sig := NewDotMatrix(5, 3), n == 0
if n == 0 {
style = g.dimmed
}
for i := 0; i < len(ns); i++ {
if ns[i] == '0' && !sig {
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, g.dimmed)
} else {
sig = true
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, style)
}
o.X += 5
}
if !ok {
printDelta(sc, dn, o, style)
}
}
// ----------------------------------------------------------------------------
// Helpers...
@ -152,8 +158,8 @@ func printDelta(sc tcell.Screen, d delta, o image.Point, s tcell.Style) {
s = s.Dim(false)
switch d {
case DeltaLess:
sc.SetContent(o.X-1, o.Y+2, '↓', nil, s)
sc.SetContent(o.X-1, o.Y+1, '↓', nil, s)
case DeltaMore:
sc.SetContent(o.X-1, o.Y+2, '↑', nil, s)
sc.SetContent(o.X-1, o.Y+1, '↑', nil, s)
}
}

View File

@ -274,8 +274,7 @@ func (a *App) switchNS(ns string) bool {
}
func (a *App) switchCtx(name string, loadPods bool) error {
log.Debug().Msgf("Switching Context %q", name)
log.Debug().Msgf("--> Switching Context %q--%q", name, a.Config.ActiveView())
a.Halt()
defer a.Resume()
{
@ -294,7 +293,11 @@ func (a *App) switchCtx(name string, loadPods bool) error {
}
a.Flash().Infof("Switching context to %s", name)
a.ReloadStyles(name)
if err := a.gotoResource("pods", "", true); loadPods && err != nil {
v := a.Config.ActiveView()
if v == "" {
v = "pod"
}
if err := a.gotoResource(v, ns, true); loadPods && err != nil {
a.Flash().Err(err)
}
a.clusterModel.Reset(a.factory)

View File

@ -58,7 +58,7 @@ func useContext(app *App, name string) error {
log.Error().Err(err).Msgf("Context switch failed")
return err
}
if err := app.switchCtx(name, false); err != nil {
if err := app.switchCtx(name, true); err != nil {
return err
}

View File

@ -36,6 +36,9 @@ type Grapheable interface {
// GetSeriesColorNames returns the series color names.
GetSeriesColorNames() []string
// SetFocusColorNames sets the focus color names.
SetFocusColorNames(fg, bg string)
// SetBackgroundColor sets chart bg color.
SetBackgroundColor(tcell.Color)
@ -80,19 +83,19 @@ func (p *Pulse) Init(ctx context.Context) error {
}
p.charts = []Grapheable{
p.makeGA(image.Point{X: 0, Y: 0}, image.Point{X: 3, Y: 2}, "apps/v1/deployments"),
p.makeGA(image.Point{X: 0, Y: 2}, image.Point{X: 3, Y: 2}, "apps/v1/replicasets"),
p.makeGA(image.Point{X: 0, Y: 4}, image.Point{X: 3, Y: 2}, "apps/v1/statefulsets"),
p.makeGA(image.Point{X: 0, Y: 6}, image.Point{X: 3, Y: 2}, "apps/v1/daemonsets"),
p.makeSP(true, image.Point{X: 3, Y: 0}, image.Point{X: 3, Y: 4}, "v1/pods"),
p.makeSP(true, image.Point{X: 3, Y: 4}, image.Point{X: 3, Y: 4}, "v1/events"),
p.makeSP(true, image.Point{X: 6, Y: 0}, image.Point{X: 3, Y: 4}, "batch/v1/jobs"),
p.makeSP(true, image.Point{X: 6, Y: 4}, image.Point{X: 3, Y: 4}, "v1/persistentvolumes"),
p.makeGA(image.Point{X: 0, Y: 0}, image.Point{X: 2, Y: 2}, "apps/v1/deployments"),
p.makeGA(image.Point{X: 0, Y: 2}, image.Point{X: 2, Y: 2}, "apps/v1/replicasets"),
p.makeGA(image.Point{X: 0, Y: 4}, image.Point{X: 2, Y: 2}, "apps/v1/statefulsets"),
p.makeGA(image.Point{X: 0, Y: 6}, image.Point{X: 2, Y: 2}, "apps/v1/daemonsets"),
p.makeSP(true, image.Point{X: 2, Y: 0}, image.Point{X: 3, Y: 2}, "v1/pods"),
p.makeSP(true, image.Point{X: 2, Y: 2}, image.Point{X: 3, Y: 2}, "v1/events"),
p.makeSP(true, image.Point{X: 2, Y: 4}, image.Point{X: 3, Y: 2}, "batch/v1/jobs"),
p.makeSP(true, image.Point{X: 2, Y: 6}, image.Point{X: 3, Y: 2}, "v1/persistentvolumes"),
}
if p.app.Conn().HasMetrics() {
p.charts = append(p.charts,
p.makeSP(false, image.Point{X: 9, Y: 0}, image.Point{X: 2, Y: 4}, "cpu"),
p.makeSP(false, image.Point{X: 9, Y: 4}, image.Point{X: 2, Y: 4}, "mem"),
p.makeSP(false, image.Point{X: 5, Y: 0}, image.Point{X: 2, Y: 4}, "cpu"),
p.makeSP(false, image.Point{X: 5, Y: 4}, image.Point{X: 2, Y: 4}, "mem"),
)
}
p.bindKeys()
@ -108,6 +111,7 @@ func (p *Pulse) Init(ctx context.Context) error {
func (p *Pulse) StylesChanged(s *config.Styles) {
p.SetBackgroundColor(s.Charts().BgColor.Color())
for _, c := range p.charts {
c.SetFocusColorNames(s.Table().BgColor.String(), s.Table().CursorColor.String())
if c.IsDial() {
c.SetBackgroundColor(s.Charts().DialBgColor.Color())
c.SetSeriesColors(s.Charts().DefaultDialColors.Colors()...)
@ -318,8 +322,9 @@ func (p *Pulse) makeSP(multi bool, loc image.Point, span image.Point, gvr string
func (p *Pulse) makeGA(loc image.Point, span image.Point, gvr string) *tchart.Gauge {
g := tchart.NewGauge(gvr)
// g.SetResolution(3)
g.SetBackgroundColor(p.app.Styles.Charts().BgColor.Color())
g.SetBorderPadding(0, 1, 0, 1)
// g.SetBorderPadding(0, 1, 0, 1)
if cc, ok := p.app.Styles.Charts().ResourceColors[gvr]; ok {
g.SetSeriesColors(cc.Colors()...)
} else {
@ -327,7 +332,7 @@ func (p *Pulse) makeGA(loc image.Point, span image.Point, gvr string) *tchart.Ga
}
g.SetLegend(fmt.Sprintf(" %s ", strings.Title(client.NewGVR(gvr).R())))
g.SetInputCapture(p.keyboard)
p.AddItem(g, loc.X, loc.Y, span.X, span.Y, span.X, span.Y, true)
p.AddItem(g, loc.X, loc.Y, span.X, span.Y, 0, 0, true)
return g
}