270 lines
5.5 KiB
Go
270 lines
5.5 KiB
Go
package ui
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/derailed/k9s/internal/config"
|
|
"github.com/derailed/k9s/internal/k8s"
|
|
"github.com/derailed/tview"
|
|
"github.com/gdamore/tcell"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// Igniter represents an initializable view.
|
|
type Igniter interface {
|
|
tview.Primitive
|
|
|
|
// Init initializes the view.
|
|
Init(ctx context.Context, ns string)
|
|
}
|
|
|
|
type (
|
|
keyHandler interface {
|
|
keyboard(evt *tcell.EventKey) *tcell.EventKey
|
|
}
|
|
|
|
// ActionsFunc augments Keybindings.
|
|
ActionsFunc func(KeyActions)
|
|
|
|
// Configurator represents an application configurations.
|
|
Configurator struct {
|
|
HasSkins bool
|
|
Config *config.Config
|
|
Styles *config.Styles
|
|
Bench *config.Bench
|
|
}
|
|
|
|
// App represents an application.
|
|
App struct {
|
|
*tview.Application
|
|
Configurator
|
|
|
|
actions KeyActions
|
|
pages *tview.Pages
|
|
content *tview.Pages
|
|
views map[string]tview.Primitive
|
|
cmdBuff *CmdBuff
|
|
}
|
|
)
|
|
|
|
// NewApp returns a new app.
|
|
func NewApp() *App {
|
|
s := App{
|
|
Application: tview.NewApplication(),
|
|
actions: make(KeyActions),
|
|
pages: tview.NewPages(),
|
|
content: tview.NewPages(),
|
|
cmdBuff: NewCmdBuff(':'),
|
|
}
|
|
|
|
s.RefreshStyles()
|
|
|
|
s.views = map[string]tview.Primitive{
|
|
"menu": NewMenuView(s.Styles),
|
|
"logo": NewLogoView(s.Styles),
|
|
"cmd": NewCmdView(s.Styles, '🐶'),
|
|
"crumbs": NewCrumbsView(s.Styles),
|
|
}
|
|
|
|
return &s
|
|
}
|
|
|
|
// Main returns main app frame.
|
|
func (a *App) Main() *tview.Pages {
|
|
return a.pages
|
|
}
|
|
|
|
// Frame returns main app content frame.
|
|
func (a *App) Frame() *tview.Pages {
|
|
return a.content
|
|
}
|
|
|
|
// Conn returns an api server connection.
|
|
func (a *App) Conn() k8s.Connection {
|
|
return a.Config.GetConnection()
|
|
}
|
|
|
|
// Init initializes the application.
|
|
func (a *App) Init() {
|
|
a.bindKeys()
|
|
a.SetInputCapture(a.keyboard)
|
|
a.cmdBuff.AddListener(a.Cmd())
|
|
a.SetRoot(a.pages, true)
|
|
}
|
|
|
|
func (a *App) bindKeys() {
|
|
a.actions = KeyActions{
|
|
KeyColon: NewKeyAction("Cmd", a.activateCmd, false),
|
|
tcell.KeyCtrlR: NewKeyAction("Redraw", a.redrawCmd, false),
|
|
tcell.KeyCtrlC: NewKeyAction("Quit", a.quitCmd, false),
|
|
tcell.KeyEscape: NewKeyAction("Escape", a.escapeCmd, false),
|
|
tcell.KeyBackspace2: NewKeyAction("Erase", a.eraseCmd, false),
|
|
tcell.KeyBackspace: NewKeyAction("Erase", a.eraseCmd, false),
|
|
tcell.KeyDelete: NewKeyAction("Erase", a.eraseCmd, false),
|
|
}
|
|
}
|
|
|
|
// BailOut exists the application.
|
|
func (a *App) BailOut() {
|
|
a.Stop()
|
|
}
|
|
|
|
// ResetCmd clear out user command.
|
|
func (a *App) ResetCmd() {
|
|
a.cmdBuff.Reset()
|
|
}
|
|
|
|
// ActivateCmd toggle command mode.
|
|
func (a *App) ActivateCmd(b bool) {
|
|
a.cmdBuff.SetActive(b)
|
|
}
|
|
|
|
// GetCmd retrieves user command.
|
|
func (a *App) GetCmd() string {
|
|
return a.cmdBuff.String()
|
|
}
|
|
|
|
// GetCmdBuff returns a cmd buffer.
|
|
func (a *App) GetCmdBuff() *CmdBuff {
|
|
return a.cmdBuff
|
|
}
|
|
|
|
// HasCmd check if cmd buffer is active and has a command.
|
|
func (a *App) HasCmd() bool {
|
|
return a.cmdBuff.IsActive() && !a.cmdBuff.Empty()
|
|
}
|
|
|
|
func (a *App) quitCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
if a.InCmdMode() {
|
|
return evt
|
|
}
|
|
a.BailOut()
|
|
|
|
return nil
|
|
}
|
|
|
|
// InCmdMode check if command mode is active.
|
|
func (a *App) InCmdMode() bool {
|
|
return a.Cmd().InCmdMode()
|
|
}
|
|
|
|
// GetActions returns a collection of actions.
|
|
func (a *App) GetActions() KeyActions {
|
|
return a.actions
|
|
}
|
|
|
|
// AddActions returns the application actions.
|
|
func (a *App) AddActions(aa KeyActions) {
|
|
for k, v := range aa {
|
|
a.actions[k] = v
|
|
}
|
|
}
|
|
|
|
// Views return the application root views.
|
|
func (a *App) Views() map[string]tview.Primitive {
|
|
return a.views
|
|
}
|
|
|
|
func (a *App) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
|
key := evt.Key()
|
|
if key == tcell.KeyRune {
|
|
if a.cmdBuff.IsActive() && evt.Modifiers() == tcell.ModNone {
|
|
a.cmdBuff.Add(evt.Rune())
|
|
return nil
|
|
}
|
|
key = asKey(evt)
|
|
}
|
|
|
|
if a, ok := a.actions[key]; ok {
|
|
log.Debug().Msgf(">> App handled key: %s", tcell.KeyNames[key])
|
|
return a.Action(evt)
|
|
}
|
|
|
|
return evt
|
|
}
|
|
|
|
func (a *App) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
if a.InCmdMode() {
|
|
return evt
|
|
}
|
|
a.Flash().Info("Command mode activated.")
|
|
a.cmdBuff.SetActive(true)
|
|
a.cmdBuff.Clear()
|
|
|
|
return nil
|
|
}
|
|
|
|
// EraseCmd removes the last char from a command.
|
|
func (a *App) eraseCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
if a.cmdBuff.IsActive() {
|
|
a.cmdBuff.Delete()
|
|
return nil
|
|
}
|
|
return evt
|
|
}
|
|
|
|
// EscapeCmd dismiss cmd mode.
|
|
func (a *App) escapeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
if a.cmdBuff.IsActive() {
|
|
a.cmdBuff.Reset()
|
|
}
|
|
return evt
|
|
}
|
|
|
|
// RedrawCmd forces a redraw.
|
|
func (a *App) redrawCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
a.Draw()
|
|
return evt
|
|
}
|
|
|
|
// ActiveView returns the currently active view.
|
|
func (a *App) ActiveView() Igniter {
|
|
return a.content.GetPrimitive("main").(Igniter)
|
|
}
|
|
|
|
// SetHints updates menu hints.
|
|
func (a *App) SetHints(h Hints) {
|
|
a.views["menu"].(*MenuView).HydrateMenu(h)
|
|
}
|
|
|
|
func (a *App) StatusReset() {
|
|
a.Logo().Reset()
|
|
a.Draw()
|
|
}
|
|
|
|
// View Accessors...
|
|
|
|
// Crumbs return app crumbs.
|
|
func (a *App) Crumbs() *CrumbsView {
|
|
return a.views["crumbs"].(*CrumbsView)
|
|
}
|
|
|
|
// Logo return the app logo.
|
|
func (a *App) Logo() *LogoView {
|
|
return a.views["logo"].(*LogoView)
|
|
}
|
|
|
|
// Flash returns app flash.
|
|
func (a *App) Flash() *FlashView {
|
|
return a.views["flash"].(*FlashView)
|
|
}
|
|
|
|
// Cmd returns app cmd.
|
|
func (a *App) Cmd() *CmdView {
|
|
return a.views["cmd"].(*CmdView)
|
|
}
|
|
|
|
// Menu returns app menu.
|
|
func (a *App) Menu() *MenuView {
|
|
return a.views["menu"].(*MenuView)
|
|
}
|
|
|
|
// AsKey converts rune to keyboard key.,
|
|
func asKey(evt *tcell.EventKey) tcell.Key {
|
|
key := tcell.Key(evt.Rune())
|
|
if evt.Modifiers() == tcell.ModAlt {
|
|
key = tcell.Key(int16(evt.Rune()) * int16(evt.Modifiers()))
|
|
}
|
|
return key
|
|
}
|