diff --git a/.golangci.yml b/.golangci.yml index ab82fa80..fb6b49e8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -79,7 +79,7 @@ linters-settings: # exclude: /path/to/file.txt funlen: - lines: 60 + lines: 65 statements: 40 govet: diff --git a/cmd/info.go b/cmd/info.go index 20754552..1f97fad2 100644 --- a/cmd/info.go +++ b/cmd/info.go @@ -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) { diff --git a/cmd/version.go b/cmd/version.go index 20c5395b..63a01156 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -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) } diff --git a/internal/tchart/component.go b/internal/tchart/component.go index 5762d765..15e85a3e 100644 --- a/internal/tchart/component.go +++ b/internal/tchart/component.go @@ -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) diff --git a/internal/tchart/dot_matrix.go b/internal/tchart/dot_matrix.go index 7e9399fb..e12c4675 100644 --- a/internal/tchart/dot_matrix.go +++ b/internal/tchart/dot_matrix.go @@ -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}, + } + } +} diff --git a/internal/tchart/dot_matrix_test.go b/internal/tchart/dot_matrix_test.go index 4fb1344b..65a9e722 100644 --- a/internal/tchart/dot_matrix_test.go +++ b/internal/tchart/dot_matrix_test.go @@ -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 = '▤', '▥' diff --git a/internal/tchart/gauge.go b/internal/tchart/gauge.go index c1e635de..a241d031 100644 --- a/internal/tchart/gauge.go +++ b/internal/tchart/gauge.go @@ -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) } } diff --git a/internal/view/app.go b/internal/view/app.go index e22ad2ad..bf2ee30b 100644 --- a/internal/view/app.go +++ b/internal/view/app.go @@ -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) diff --git a/internal/view/context.go b/internal/view/context.go index fce01e41..802b70c3 100644 --- a/internal/view/context.go +++ b/internal/view/context.go @@ -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 } diff --git a/internal/view/pulse.go b/internal/view/pulse.go index 38be031b..3205c6c6 100644 --- a/internal/view/pulse.go +++ b/internal/view/pulse.go @@ -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 }