Unverified Commit 78d3a48c authored by Elf's avatar Elf Committed by GitHub
Browse files

Merge pull request #17 from TNK-Studio/dev

feat: add resource filter dialog.
Showing with 1148 additions and 668 deletions
+1148 -668
name: Changelog CI
on:
pull_request:
types: [opened, reopened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- name: Run Changelog CI
uses: saadmk11/changelog-ci@v0.7.0
with:
changelog_filename: CHANGELOG.md
config_file: .github/workflows/config/changelog-ci-config.json
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
name: CI
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v1
with:
go-version: 1.14
- uses: actions/checkout@v2.3.4
- run: go mod download
- name: Build
run: cd ./cmd/lazykube && go build -v .
- name: Lint
uses: Jerome1337/golint-action@v1.0.2
with:
golint-path: ./
- name: Go report card
# You may pin to the exact commit or the version.
# uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9
uses: creekorful/goreportcard-action@v1.0
\ No newline at end of file
### Changelog
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v0.1.2](https://github.com/TNK-Studio/lazykube/compare/v0.1.1...v0.1.2)
> 17 November 2020
- fix: #15 [`#16`](https://github.com/TNK-Studio/lazykube/pull/16)
- chore: modify actions [`#14`](https://github.com/TNK-Studio/lazykube/pull/14)
- chore: add changelog ci [`#11`](https://github.com/TNK-Studio/lazykube/pull/11)
- docs: update readme. add faq [`#10`](https://github.com/TNK-Studio/lazykube/pull/10)
- fix: Fixed a BUG where the program quits when the K8s API Server returns an error [`4dd8f9a`](https://github.com/TNK-Studio/lazykube/commit/4dd8f9a6222d1a25bb82443e26cad23bf226a956)
- chore: add gitee mirror action [`25fd0f0`](https://github.com/TNK-Studio/lazykube/commit/25fd0f038ce07c41f9d6d4b796a4c8f71f50bacb)
#### [v0.1.1](https://github.com/TNK-Studio/lazykube/compare/v0.1.0...v0.1.1)
> 16 November 2020
- Dev [`#9`](https://github.com/TNK-Studio/lazykube/pull/9)
- docs: modify readme [`c6091be`](https://github.com/TNK-Studio/lazykube/commit/c6091be52028d6860054cc3956e6f19bf4d43abb)
- fix: previous line bug [`42e6c50`](https://github.com/TNK-Studio/lazykube/commit/42e6c506756326d8271bcb21b7fe7164cc8b409b)
- fix: Optimize log rendering [`ec1ba69`](https://github.com/TNK-Studio/lazykube/commit/ec1ba69e773b00da2a5128d9f9111ba411e2f63e)
#### v0.1.0
> 15 November 2020
- feat: modify render plot [`#7`](https://github.com/TNK-Studio/lazykube/pull/7)
- Dev [`#6`](https://github.com/TNK-Studio/lazykube/pull/6)
- Dev [`#5`](https://github.com/TNK-Studio/lazykube/pull/5)
- feat: add kubecli and navigation actions. [`#4`](https://github.com/TNK-Studio/lazykube/pull/4)
- feat: add Navigation View [`#3`](https://github.com/TNK-Studio/lazykube/pull/3)
- feat: add ViewClickHandler [`#2`](https://github.com/TNK-Studio/lazykube/pull/2)
- Dev [`#1`](https://github.com/TNK-Studio/lazykube/pull/1)
- init [`e60a15a`](https://github.com/TNK-Studio/lazykube/commit/e60a15aaed1232b328bab2896657cd7abe92b17f)
- feat: add kubecli.KubeCLI [`453259d`](https://github.com/TNK-Studio/lazykube/commit/453259dd37c54ed11562cadea63d92fd3c595f7b)
- fix: fix detail view [`5f27486`](https://github.com/TNK-Studio/lazykube/commit/5f2748638b318b24e68e734b5e94446f1d964760)
......@@ -2,14 +2,17 @@ module github.com/TNK-Studio/lazykube
go 1.14
replace github.com/jroimartin/gocui v0.4.0 => github.com/elfgzp/gocui v0.4.1-0.20201118030412-21fac610f2e0
require (
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/gojp/goreportcard v0.0.0-20201106142952-232d912e513e // indirect
github.com/gookit/color v1.3.2
github.com/guptarohit/asciigraph v0.5.1
github.com/imdario/mergo v0.3.8 // indirect
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
github.com/jesseduffield/lazydocker v0.9.1 // indirect
github.com/jroimartin/gocui v0.4.0
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
......@@ -17,12 +20,11 @@ require (
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.0.0
github.com/spkg/bom v1.0.0
google.golang.org/appengine v1.6.5
github.com/tebeka/strftime v0.1.5 // indirect
k8s.io/api v0.19.3
k8s.io/apimachinery v0.19.3
k8s.io/cli-runtime v0.19.3
k8s.io/client-go v0.19.3
k8s.io/klog v1.0.0 // indirect
k8s.io/klog/v2 v2.2.0
k8s.io/kubectl v0.19.3
k8s.io/metrics v0.19.3
......
This diff is collapsed.
......@@ -3,13 +3,7 @@ package app
import (
guilib "github.com/TNK-Studio/lazykube/pkg/gui"
"github.com/TNK-Studio/lazykube/pkg/kubecli"
"github.com/TNK-Studio/lazykube/pkg/log"
"github.com/jroimartin/gocui"
"math"
)
const (
selectedViewLine = "selectedViewLine"
)
var (
......@@ -22,13 +16,6 @@ var (
Mod: gocui.ModNone,
}
//previousCyclicView = &guilib.Action{
// Name: "previousCyclicView",
// Key: gocui.KeyArrowUp,
// Handler: previousCyclicViewHandler,
// Mod: gocui.ModNone,
//}
backToPreviousView = &guilib.Action{
Name: "backToPreviousView",
Key: gocui.KeyEsc,
......@@ -47,133 +34,75 @@ var (
}
previousLine = &guilib.Action{
Name: "previousLine",
Key: gocui.KeyArrowUp,
Handler: previousLineHandler,
Mod: gocui.ModNone,
ReRender: true,
Name: "previousLine",
Key: gocui.KeyArrowUp,
Handler: previousLineHandler,
Mod: gocui.ModNone,
}
nextLine = &guilib.Action{
Name: "nextLine",
Key: gocui.KeyArrowDown,
Handler: nextLineHandler,
Mod: gocui.ModNone,
ReRender: true,
Name: "nextLine",
Key: gocui.KeyArrowDown,
Handler: nextLineHandler,
Mod: gocui.ModNone,
}
actions = []*guilib.Action{
backToPreviousView,
&guilib.Action{
Name: "scrollUp",
{
Name: "previousPage",
Keys: []interface{}{
gocui.KeyPgup,
},
Handler: previousPageHandler,
Mod: gocui.ModNone,
},
{
Name: "nextPage",
Keys: []interface{}{
gocui.KeyPgdn,
},
Handler: nextPageHandler,
Mod: gocui.ModNone,
},
{
Name: "scrollUp",
Keys: []interface{}{
gocui.MouseWheelUp,
},
Handler: scrollUpHandler,
Mod: gocui.ModNone,
},
&guilib.Action{
{
Name: "scrollDown",
Keys: []interface{}{
gocui.KeyPgdn,
gocui.MouseWheelDown,
},
Handler: scrollDownHandler,
Mod: gocui.ModNone,
},
&guilib.Action{
{
Name: "scrollTop",
Key: gocui.KeyHome,
Handler: scrollTopHandler,
Mod: gocui.ModNone,
},
&guilib.Action{
{
Name: "scrollBottom",
Key: gocui.KeyEnd,
Handler: scrollBottomHandler,
Mod: gocui.ModNone,
},
}
)
viewActionsMap = map[string][]*guilib.Action{
navigationViewName: []*guilib.Action{
&guilib.Action{
Name: "navigationArrowLeft",
Key: gocui.KeyArrowLeft,
Handler: navigationArrowLeftHandler,
Mod: gocui.ModNone,
ReRender: true,
},
&guilib.Action{
Name: "navigationArrowRight",
Key: gocui.KeyArrowRight,
Handler: navigationArrowRightHandler,
Mod: gocui.ModNone,
ReRender: true,
},
&guilib.Action{
Name: "navigationDown",
Key: gocui.KeyArrowDown,
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
gui.FocusView(detailViewName, false)
return nil
}
},
Mod: gocui.ModNone,
ReRender: true,
},
},
detailViewName: []*guilib.Action{
&guilib.Action{
Name: "detailArrowUp",
Key: gocui.KeyArrowUp,
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
gui.FocusView(navigationViewName, false)
return nil
}
},
Mod: gocui.ModNone,
ReRender: true,
},
},
clusterInfoViewName: []*guilib.Action{
toNavigation,
nextCyclicView,
//previousCyclicView,
},
namespaceViewName: []*guilib.Action{
toNavigation,
nextCyclicView,
//previousCyclicView,
previousLine,
nextLine,
},
serviceViewName: []*guilib.Action{
toNavigation,
nextCyclicView,
//previousCyclicView,
previousLine,
nextLine,
},
deploymentViewName: []*guilib.Action{
toNavigation,
nextCyclicView,
//previousCyclicView,
previousLine,
nextLine,
},
podViewName: []*guilib.Action{
toNavigation,
nextCyclicView,
//previousCyclicView,
previousLine,
nextLine,
},
func setViewSelectedLine(gui *guilib.Gui, view *guilib.View, selectedLine string) error {
formatted := formatResourceName(selectedLine, 0)
if notResourceSelected(formatted) {
formatted = ""
}
)
return nil
}
func switchNamespace(gui *guilib.Gui, selectedNamespaceLine string) {
kubecli.Cli.SetNamespace(selectedNamespaceLine)
......@@ -191,241 +120,5 @@ func switchNamespace(gui *guilib.Gui, selectedNamespaceLine string) {
}
detailView.Autoscroll = false
detailView.SetOrigin(0, 0)
}
func nextCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
for index, viewName := range cyclicViews {
if currentView.Name == viewName {
nextIndex := index + 1
if nextIndex >= len(cyclicViews) {
nextIndex = 0
}
nextViewName := cyclicViews[nextIndex]
log.Logger.Debugf("nextCyclicViewHandler - nextViewName: %s", nextViewName)
return gui.FocusView(nextViewName, true)
}
}
return nil
}
}
func previousCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
for index, viewName := range cyclicViews {
if currentView.Name == viewName {
nextIndex := index - 1
if nextIndex < 0 {
nextIndex = len(cyclicViews) - 1
}
previousViewName := cyclicViews[nextIndex]
log.Logger.Debugf("previousCyclicViewHandler - previousViewName: %s", previousViewName)
return gui.FocusView(cyclicViews[nextIndex], true)
}
}
return nil
}
}
func backToPreviousViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
if gui.HasPreviousView() {
return gui.ReturnPreviousView()
}
return gui.FocusView(clusterInfoViewName, false)
}
}
func toNavigationHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
return gui.FocusView(navigationViewName, true)
}
}
func navigationArrowRightHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
options := viewNavigationMap[activeView.Name]
if navigationIndex+1 >= len(options) {
return nil
}
switchNavigation(navigationIndex + 1)
return nil
}
}
func navigationArrowLeftHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
if navigationIndex-1 < 0 {
return gui.ReturnPreviousView()
}
switchNavigation(navigationIndex - 1)
return nil
}
}
func scrollUpHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, oy := v.Origin()
newOy := int(math.Max(0, float64(oy-2)))
return v.SetOrigin(ox, newOy)
}
}
func scrollDownHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, oy := v.Origin()
reservedLines := 0
_, sizeY := v.Size()
reservedLines = sizeY
totalLines := len(v.ViewBufferLines())
if oy+reservedLines >= totalLines {
v.Autoscroll = true
return nil
}
return v.SetOrigin(ox, oy+2)
}
}
func scrollTopHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, _ := v.Origin()
return v.SetOrigin(ox, 0)
}
}
func scrollBottomHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
totalLines := len(v.ViewBufferLines())
if totalLines == 0 {
return nil
}
_, vy := v.Size()
if totalLines <= vy {
return nil
}
ox, _ := v.Origin()
v.Autoscroll = true
return v.SetOrigin(ox, totalLines-1)
}
}
func viewLineClickHandler(gui *guilib.Gui, view *guilib.View, cy int, lineString string) error {
detailView, _ := gui.GetView(detailViewName)
if detailView != nil {
detailView.SetOrigin(0, 0)
}
if cy == 0 {
selected := formatSelectedName(lineString, 0)
if notResourceSelected(selected) {
log.Logger.Debugf("viewLineClickHandler - view: '%s' cy == 0, view.State.Set(selectedViewLine, nil)", view.Name)
if view.Name == namespaceViewName {
switchNamespace(gui, "")
}
return view.State.Set(selectedViewLine, nil)
}
}
log.Logger.Debugf("viewLineClickHandler - view: '%s' view.State.Set(selectedViewLine, \"%s\")", view.Name, lineString)
if view.Name == namespaceViewName {
namespace := formatSelectedNamespace(lineString)
log.Logger.Debugf("viewLineClickHandler - switch namespace to %s", namespace)
switchNamespace(gui, namespace)
}
return view.State.Set(selectedViewLine, lineString)
}
func previousLineHandler(gui *guilib.Gui) func(gui *gocui.Gui, view *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
_, cy := v.Cursor()
ox, oy := v.Origin()
var lineStr string
var err error
if cy-1 < 0 {
if err := v.SetOrigin(ox, int(math.Max(0, float64(oy-1)))); err != nil {
return err
}
v.MoveCursor(0, -1, false)
_, newCy := v.Cursor()
lineStr, err = v.Line(newCy)
if err != nil {
log.Logger.Warningf("previousLineHandler - v.Line(cy - 1)", cy)
}
} else {
lineStr, err = v.Line(cy - 1)
if err != nil {
log.Logger.Warningf("previousLineHandler - v.Line(cy - 1)", cy)
}
v.MoveCursor(0, -1, false)
}
formatted := formatSelectedName(lineStr, 0)
if formatted != "NAME" && formatted != "NAMESPACE" {
if currentView.Name == namespaceViewName {
namespace := formatSelectedNamespace(lineStr)
log.Logger.Debugf("previousLineHandler - switch namespace to %s", namespace)
switchNamespace(gui, namespace)
}
return currentView.State.Set(selectedViewLine, lineStr)
}
if currentView.Name == namespaceViewName {
namespace := ""
log.Logger.Debugf("previousLineHandler - switch namespace to %s", namespace)
switchNamespace(gui, namespace)
return currentView.State.Set(selectedViewLine, nil)
}
return currentView.State.Set(selectedViewLine, lineStr)
}
}
func nextLineHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
_, cy := v.Cursor()
lineStr, err := v.Line(cy + 1)
if err != nil {
log.Logger.Warningf("nextLineHandler - v.Line(%d + 1)", cy)
}
v.MoveCursor(0, 1, false)
if currentView.Name == namespaceViewName {
namespace := formatSelectedNamespace(lineStr)
log.Logger.Debugf("nextLineHandler - switch namespace to %s", namespace)
switchNamespace(gui, namespace)
}
if lineStr == "" {
return currentView.State.Set(selectedViewLine, nil)
}
return currentView.State.Set(selectedViewLine, lineStr)
}
gui.ReRenderViews(namespaceViewName, serviceViewName, deploymentViewName, podViewName, navigationViewName, detailViewName)
}
......@@ -2,23 +2,25 @@ package app
import (
"github.com/TNK-Studio/lazykube/pkg/config"
"github.com/TNK-Studio/lazykube/pkg/gui"
guilib "github.com/TNK-Studio/lazykube/pkg/gui"
"github.com/TNK-Studio/lazykube/pkg/utils"
"github.com/jroimartin/gocui"
)
// App lazykube application
type App struct {
ClusterInfo *gui.View
Namespace *gui.View
Service *gui.View
Deployment *gui.View
Pod *gui.View
Navigation *gui.View
Detail *gui.View
Option *gui.View
Gui *gui.Gui
ClusterInfo *guilib.View
Namespace *guilib.View
Service *guilib.View
Deployment *guilib.View
Pod *guilib.View
Navigation *guilib.View
Detail *guilib.View
Option *guilib.View
Gui *guilib.Gui
}
// NewApp new lazykube application
func NewApp() *App {
app := &App{
ClusterInfo: ClusterInfo,
......@@ -39,7 +41,7 @@ func NewApp() *App {
Mouse: true,
InputEsc: true,
}
app.Gui = gui.NewGui(
app.Gui = guilib.NewGui(
conf,
app.ClusterInfo,
app.Namespace,
......@@ -52,27 +54,22 @@ func NewApp() *App {
)
app.Gui.OnRender = app.OnRender
app.Gui.OnRenderOptions = app.OnRenderOptions
app.Gui.Actions = actions
return app
}
// Run run
func (app *App) Run() {
for _, act := range actions {
app.Gui.BindAction("", act)
}
for viewName, actArr := range viewActionsMap {
for _, act := range actArr {
app.Gui.BindAction(viewName, act)
}
}
app.Gui.Run()
}
// Stop stop
func (app *App) Stop() {
app.Gui.Close()
}
func (app *App) OnRender(gui *gui.Gui) error {
// OnRender OnRender
func (app *App) OnRender(gui *guilib.Gui) error {
if gui.MaxHeight() < 28 {
for _, viewName := range functionViews {
if _, err := gui.SetViewOnTop(viewName); err != nil {
......@@ -89,17 +86,20 @@ func (app *App) OnRender(gui *gui.Gui) error {
return nil
}
func (app *App) OnRenderOptions(gui *gui.Gui) error {
// OnRenderOptions OnRenderOptions
func (app *App) OnRenderOptions(gui *guilib.Gui) error {
return gui.RenderString(
app.Option.Name,
utils.OptionsMapToString(
map[string]string{
"← → ↑ ↓": "navigate",
"Ctrl+c": "close",
"Ctrl+c": "exit",
"Esc": "back",
"PgUp/PgDn": "scroll",
"Home/End": "top/bottom",
"Tab": "next panel",
"F4": "filter",
"m": "menu",
}),
)
}
package app
import (
"fmt"
guilib "github.com/TNK-Studio/lazykube/pkg/gui"
"github.com/TNK-Studio/lazykube/pkg/log"
"github.com/TNK-Studio/lazykube/pkg/utils"
"github.com/jroimartin/gocui"
"strings"
)
const (
filterInputViewName = "filterInput"
filterInputValueStateKey = "value"
filteredViewName = "filtered"
filteredNoResource = "No Resource."
)
var (
toFilteredView = &guilib.Action{
Name: "toFiltered",
Keys: []interface{}{
gocui.KeyTab,
gocui.KeyArrowDown,
},
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
return gui.FocusView(filteredViewName, false)
}
},
Mod: gocui.ModNone,
}
toFilterInputView = &guilib.Action{
Name: "toFilterInput",
Keys: []interface{}{
gocui.KeyTab,
},
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
return gui.FocusView(filterInputViewName, false)
}
},
Mod: gocui.ModNone,
}
filteredNextLine = &guilib.Action{
Name: "filteredNextLine",
Keys: []interface{}{
gocui.KeyArrowDown,
},
Handler: nextLineHandler,
Mod: gocui.ModNone,
}
filteredPreviousLine = &guilib.Action{
Name: "filteredPreviousLine",
Keys: []interface{}{
gocui.KeyArrowUp,
},
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
_, oy := v.Origin()
_, cy := v.Cursor()
if oy == 0 && cy-1 < 0 {
return gui.FocusView(filterInputViewName, false)
}
return previousLineHandler(gui)(g, v)
}
},
Mod: gocui.ModNone,
}
)
func newConfirmFilterInput(resourceViewName string) *guilib.Action {
confirmFilterInput := &guilib.Action{
Name: "confirmFilterInput",
Key: gocui.KeyEnter,
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
filteredView, err := gui.GetView(filteredViewName)
if err != nil {
return err
}
_, cy := filteredView.Cursor()
filtered, _ := filteredView.Line(cy)
if filtered == "" || filtered == filteredNoResource {
return nil
}
resourceView, err := gui.GetView(resourceViewName)
if err != nil {
return err
}
y := resourceView.WhichLine(filtered)
if y < 0 {
if err := resourceView.ResetCursorOrigin(); err != nil {
return err
}
} else {
if err := resourceView.SetOrigin(0, y); err != nil {
return err
}
if err := resourceView.SetCursor(0, 0); err != nil {
return err
}
}
if err := closeFilterDialog(gui); err != nil {
return err
}
if err := gui.ReturnPreviousView(); err != nil {
return err
}
return nil
}
},
Mod: gocui.ModNone,
}
return confirmFilterInput
}
func newFilterDialog(title string, gui *guilib.Gui, resourceViewName string) error {
confirmFilterInput := newConfirmFilterInput(resourceViewName)
filterInput := &guilib.View{
Name: filterInputViewName,
Title: title,
CanNotReturn: true,
AlwaysOnTop: true,
Clickable: true,
Editable: true,
MouseDisable: true,
DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) {
maxWidth, maxHeight := gui.Size()
quarterWidth, quarterHeight := maxWidth/4, maxHeight/4
x0 := quarterWidth
x1 := quarterWidth * 3
y0 := quarterHeight
y1 := quarterHeight + 3
return x0, y0, x1, y1
},
Actions: []*guilib.Action{
toFilteredView,
confirmFilterInput,
},
OnRender: func(gui *guilib.Gui, view *guilib.View) error {
gui.Config.Cursor = true
gui.Configure()
return nil
},
OnRenderOptions: filterDialogRenderOption,
OnFocusLost: func(gui *guilib.Gui, view *guilib.View) error {
gui.Config.Cursor = false
gui.Configure()
return filterDialogFocusLost(gui, view)
},
OnFocus: func(gui *guilib.Gui, view *guilib.View) error {
gui.Config.Cursor = true
gui.Configure()
return nil
},
OnEditedChange: func(gui *guilib.Gui, view *guilib.View, key gocui.Key, ch rune, mod gocui.Modifier) {
var value string
bufferLines := view.ViewBufferLines()
if len(bufferLines) > 0 {
value = view.ViewBufferLines()[0]
}
if err := view.State.Set(filterInputValueStateKey, value); err != nil {
log.Logger.Warningf("OnEditedChange - view.State.Set(filterInputValueStateKey,%s) error %s", value, err)
return
}
filteredView, err := gui.GetView(filteredViewName)
if err != nil {
log.Logger.Warningf("filteredView - gui.GetView(filteredViewName) error %s", err)
return
}
filteredView.ReRender()
},
}
filtered := &guilib.View{
Name: filteredViewName,
Clickable: true,
CanNotReturn: true,
AlwaysOnTop: true,
Highlight: true,
SelFgColor: gocui.ColorBlack,
SelBgColor: gocui.ColorWhite,
Actions: []*guilib.Action{
toFilterInputView,
confirmFilterInput,
filteredNextLine,
filteredPreviousLine,
},
OnRender: func(gui *guilib.Gui, view *guilib.View) error {
value := ""
val, _ := filterInput.State.Get(filterInputValueStateKey)
if val != nil {
value = val.(string)
}
view.Clear()
if err := view.ResetCursorOrigin(); err != nil {
log.Logger.Warningf("OnRender - view %s view.ResetCursorOrigin() error %s", filterInputViewName, err)
return err
}
resourceView, err := gui.GetView(resourceViewName)
if err != nil {
return err
}
resourceList := resourceView.ViewBufferLines()
if len(resourceList) == 0 {
return nil
}
if value == "" {
fmt.Fprint(view, strings.Join(resourceList[1:], "\n"))
return nil
}
filtered := make([]string, 0)
value = strings.ToLower(value)
for _, resource := range resourceList[1:] {
if strings.Index(strings.ToLower(resource), value) > -1 {
filtered = append(filtered, resource)
}
}
if len(filtered) == 0 {
fmt.Fprint(view, filteredNoResource)
return nil
}
fmt.Fprint(view, strings.Join(filtered, "\n"))
return nil
},
OnRenderOptions: filterDialogRenderOption,
OnFocusLost: filterDialogFocusLost,
OnLineClick: func(gui *guilib.Gui, view *guilib.View, cy int, lineString string) error {
return nil
},
DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) {
maxWidth, maxHeight := gui.Size()
quarterWidth, quarterHeight := maxWidth/4, maxHeight/4
x0 := quarterWidth
x1 := quarterWidth * 3
y0 := quarterHeight + 2
y1 := quarterHeight * 3
return x0, y0, x1, y1
},
}
if err := gui.AddView(filterInput); err != nil {
return err
}
if err := gui.AddView(filtered); err != nil {
return err
}
if _, err := gui.SetViewOnTop(filterInput.Name); err != nil {
return err
}
if _, err := gui.SetViewOnTop(filtered.Name); err != nil {
return err
}
if err := gui.FocusView(filterInput.Name, true); err != nil {
return err
}
return nil
}
func filterDialogRenderOption(gui *guilib.Gui, view *guilib.View) error {
return gui.RenderString(
optionViewName,
utils.OptionsMapToString(
map[string]string{
"← → ↑ ↓": "navigate",
"Ctrl+c": "exit",
"Esc": "close dialog",
"PgUp/PgDn": "scroll",
"Home/End": "top/bottom",
"Tab": "next panel",
"Enter": "confirm",
}),
)
}
func filterDialogFocusLost(gui *guilib.Gui, view *guilib.View) error {
currentView := gui.CurrentView()
if currentView != nil && (currentView.Name == filterInputViewName || currentView.Name == filteredViewName) {
return nil
}
if err := closeFilterDialog(gui); err != nil {
return err
}
return nil
}
func closeFilterDialog(gui *guilib.Gui) error {
if err := gui.DeleteView(filterInputViewName); err != nil {
return err
}
if err := gui.DeleteView(filteredViewName); err != nil {
return err
}
gui.Config.Cursor = false
gui.Configure()
return nil
}
......@@ -6,10 +6,10 @@ import (
)
func formatSelectedNamespace(selected string) string {
return formatSelectedName(selected, 0)
return formatResourceName(selected, 0)
}
func formatSelectedName(selected string, index int) string {
func formatResourceName(selected string, index int) string {
if selected == "" {
return ""
}
......
package app
import (
"fmt"
guilib "github.com/TNK-Studio/lazykube/pkg/gui"
"github.com/TNK-Studio/lazykube/pkg/log"
"github.com/jroimartin/gocui"
"math"
)
func nextCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
for index, viewName := range cyclicViews {
if currentView.Name == viewName {
nextIndex := index + 1
if nextIndex >= len(cyclicViews) {
nextIndex = 0
}
nextViewName := cyclicViews[nextIndex]
log.Logger.Debugf("nextCyclicViewHandler - nextViewName: %s", nextViewName)
return gui.FocusView(nextViewName, true)
}
}
return nil
}
}
func backToPreviousViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
if gui.HasPreviousView() {
return gui.ReturnPreviousView()
}
return gui.FocusView(clusterInfoViewName, false)
}
}
func toNavigationHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
return gui.FocusView(navigationViewName, true)
}
}
func navigationArrowRightHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
options := viewNavigationMap[activeView.Name]
if navigationIndex+1 >= len(options) {
return nil
}
switchNavigation(navigationIndex + 1)
gui.ReRenderViews(navigationViewName, detailViewName)
return nil
}
}
func navigationArrowLeftHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, view *gocui.View) error {
if navigationIndex-1 < 0 {
return gui.ReturnPreviousView()
}
switchNavigation(navigationIndex - 1)
gui.ReRenderViews(navigationViewName, detailViewName)
return nil
}
}
func nextPageHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, oy := v.Origin()
_, height := v.Size()
newOy := int(math.Min(float64(len(v.ViewBufferLines())), float64(oy+height)))
return v.SetOrigin(ox, newOy)
}
}
func previousPageHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, oy := v.Origin()
_, height := v.Size()
newOy := int(math.Max(0, float64(oy-height)))
return v.SetOrigin(ox, newOy)
}
}
func scrollUpHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, oy := v.Origin()
newOy := int(math.Max(0, float64(oy-2)))
return v.SetOrigin(ox, newOy)
}
}
func scrollDownHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, oy := v.Origin()
reservedLines := 0
_, sizeY := v.Size()
reservedLines = sizeY
totalLines := len(v.ViewBufferLines())
if oy+reservedLines >= totalLines {
v.Autoscroll = true
return nil
}
return v.SetOrigin(ox, oy+2)
}
}
func scrollTopHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
v.Autoscroll = false
ox, _ := v.Origin()
return v.SetOrigin(ox, 0)
}
}
func scrollBottomHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
totalLines := len(v.ViewBufferLines())
if totalLines == 0 {
return nil
}
_, vy := v.Size()
if totalLines <= vy {
return nil
}
ox, _ := v.Origin()
v.Autoscroll = true
return v.SetOrigin(ox, totalLines-1)
}
}
func previousLineHandler(gui *guilib.Gui) func(gui *gocui.Gui, view *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
_, height := v.Size()
cx, cy := v.Cursor()
ox, oy := v.Origin()
if cy-1 <= 0 && oy-1 > 0 {
v.SetOrigin(ox, int(math.Max(0, float64(oy-height+1))))
v.SetCursor(cx, height-1)
return nil
}
v.MoveCursor(0, -1, false)
return nil
}
}
func nextLineHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
currentView := gui.CurrentView()
if currentView == nil {
return nil
}
_, height := v.Size()
cx, cy := v.Cursor()
if cy+1 >= height-1 {
ox, oy := v.Origin()
v.SetOrigin(ox, oy+height-1)
v.SetCursor(cx, 0)
return nil
}
v.MoveCursor(0, 1, false)
return nil
}
}
func newFilterAction(viewName string, resourceName string) *guilib.Action {
return &guilib.Action{
Name: fmt.Sprintf("%sFilterAction", viewName),
Key: gocui.KeyF4,
Handler: func(gui *guilib.Gui) func(g *gocui.Gui, v *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
if err := newFilterDialog(fmt.Sprintf("Input to filter %s", resourceName), gui, viewName); err != nil {
return err
}
return nil
}
},
Mod: gocui.ModNone,
}
}
func viewSelectedLineChangeHandler(gui *guilib.Gui, view *guilib.View, selectedLine string) error {
if err := setViewSelectedLine(gui, view, selectedLine); err != nil {
return err
}
gui.ReRenderViews(view.Name, navigationViewName, detailViewName)
return nil
}
package app
import (
"github.com/TNK-Studio/lazykube/pkg/gui"
guilib "github.com/TNK-Studio/lazykube/pkg/gui"
"github.com/jroimartin/gocui"
)
......@@ -17,97 +17,159 @@ const (
)
var (
ClusterInfo = &gui.View{
ClusterInfo = &guilib.View{
Name: clusterInfoViewName,
Title: "Cluster Info",
Clickable: true,
LowerRightPointXFunc: func(gui *gui.Gui, view *gui.View) int {
LowerRightPointXFunc: func(gui *guilib.Gui, view *guilib.View) int {
return leftSideWidth(gui.MaxWidth())
},
LowerRightPointYFunc: reactiveHeight,
OnRender: renderClusterInfo,
Actions: []*guilib.Action{
toNavigation,
nextCyclicView,
},
}
Deployment = &gui.View{
Name: deploymentViewName,
Title: "Deployments",
FgColor: gocui.ColorDefault,
Clickable: true,
//Highlight: true,
//SelFgColor: gocui.ColorGreen,
//SelBgColor: gocui.ColorDefault,
OnRender: deploymentRender,
OnLineClick: viewLineClickHandler,
OnFocus: func(gui *gui.Gui, view *gui.View) error {
Deployment = &guilib.View{
Name: deploymentViewName,
Title: "Deployments",
FgColor: gocui.ColorDefault,
Clickable: true,
Highlight: true,
SelFgColor: gocui.ColorGreen,
OnRender: deploymentRender,
OnSelectedLineChange: viewSelectedLineChangeHandler,
OnFocus: func(gui *guilib.Gui, view *guilib.View) error {
if err := onFocusClearSelected(gui, view); err != nil {
return err
}
return nil
},
DimensionFunc: gui.BeneathView(
DimensionFunc: guilib.BeneathView(
serviceViewName,
reactiveHeight,
migrateTopFunc,
),
Actions: []*guilib.Action{
toNavigation,
nextCyclicView,
previousLine,
nextLine,
newFilterAction(deploymentViewName, "deployments"),
},
}
Navigation = &gui.View{
Navigation = &guilib.View{
Name: navigationViewName,
Title: "Navigation",
Clickable: true,
CanNotReturn: true,
OnClick: navigationOnClick,
FgColor: gocui.ColorGreen,
DimensionFunc: func(gui *gui.Gui, view *gui.View) (int, int, int, int) {
DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) {
return leftSideWidth(gui.MaxWidth()) + 1, 0, gui.MaxWidth() - 1, 2
},
OnRender: navigationRender,
Actions: []*guilib.Action{
{
Name: "navigationArrowLeft",
Key: gocui.KeyArrowLeft,
Handler: navigationArrowLeftHandler,
Mod: gocui.ModNone,
},
{
Name: "navigationArrowRight",
Key: gocui.KeyArrowRight,
Handler: navigationArrowRightHandler,
Mod: gocui.ModNone,
},
{
Name: "navigationDown",
Key: gocui.KeyArrowDown,
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
if err := gui.FocusView(detailViewName, false); err != nil {
return err
}
return nil
}
},
Mod: gocui.ModNone,
},
},
}
Detail = &gui.View{
Detail = &guilib.View{
Name: detailViewName,
Wrap: true,
Title: "",
Clickable: true,
OnRender: detailRender,
//OnFocusLost: func(gui *gui.Gui, view *gui.View) error {
// if err := view.SetCursor(0, 0); err != nil {
// return err
// }
//
// return nil
//},
DimensionFunc: func(gui *gui.Gui, view *gui.View) (int, int, int, int) {
DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) {
return leftSideWidth(gui.MaxWidth()) + 1, 2, gui.MaxWidth() - 1, gui.MaxHeight() - 2
},
Actions: []*guilib.Action{
{
Name: "detailArrowUp",
Key: gocui.KeyArrowUp,
Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error {
return func(*gocui.Gui, *gocui.View) error {
gui.FocusView(navigationViewName, false)
return nil
}
},
Mod: gocui.ModNone,
},
},
}
Namespace = &gui.View{
Namespace = &guilib.View{
Name: namespaceViewName,
Title: "Namespaces",
Clickable: true,
//Highlight: true,
//SelFgColor: gocui.ColorGreen,
//SelBgColor: gocui.ColorDefault,
OnRender: namespaceRender,
OnLineClick: viewLineClickHandler,
OnFocus: func(gui *gui.Gui, view *gui.View) error {
OnRender: namespaceRender,
OnSelectedLineChange: func(gui *guilib.Gui, view *guilib.View, selectedLine string) error {
formatted := formatResourceName(selectedLine, 0)
if notResourceSelected(formatted) {
formatted = ""
}
if formatted == "" {
switchNamespace(gui, "")
return nil
} else {
switchNamespace(gui, formatSelectedNamespace(selectedLine))
}
return nil
},
Highlight: true,
SelFgColor: gocui.ColorGreen,
OnFocus: func(gui *guilib.Gui, view *guilib.View) error {
if err := onFocusClearSelected(gui, view); err != nil {
return err
}
return nil
},
FgColor: gocui.ColorDefault,
DimensionFunc: gui.BeneathView(
DimensionFunc: guilib.BeneathView(
clusterInfoViewName,
reactiveHeight,
migrateTopFunc,
),
Actions: []*guilib.Action{
toNavigation,
nextCyclicView,
previousLine,
nextLine,
newFilterAction(namespaceViewName, "namespaces"),
},
}
Option = &gui.View{
Option = &guilib.View{
Name: optionViewName,
DimensionFunc: func(gui *gui.Gui, view *gui.View) (int, int, int, int) {
DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) {
maxWidth, maxHeight := gui.Size()
return 0, maxHeight - 2, maxWidth, maxHeight
},
......@@ -115,48 +177,60 @@ var (
FgColor: gocui.ColorBlue,
}
Pod = &gui.View{
Name: podViewName,
Title: "Pods",
Clickable: true,
//Highlight: true,
//SelFgColor: gocui.ColorGreen,
//SelBgColor: gocui.ColorDefault,
OnRender: podRender,
OnLineClick: viewLineClickHandler,
OnFocus: func(gui *gui.Gui, view *gui.View) error {
Pod = &guilib.View{
Name: podViewName,
Title: "Pods",
Clickable: true,
OnRender: podRender,
OnSelectedLineChange: viewSelectedLineChangeHandler,
Highlight: true,
SelFgColor: gocui.ColorGreen,
OnFocus: func(gui *guilib.Gui, view *guilib.View) error {
if err := onFocusClearSelected(gui, view); err != nil {
return err
}
return nil
},
FgColor: gocui.ColorDefault,
DimensionFunc: gui.BeneathView(
DimensionFunc: guilib.BeneathView(
deploymentViewName,
reactiveHeight,
migrateTopFunc,
),
Actions: []*guilib.Action{
toNavigation,
nextCyclicView,
previousLine,
nextLine,
newFilterAction(podViewName, "pods"),
},
}
Service = &gui.View{
Name: serviceViewName,
Title: "Services",
Clickable: true,
//Highlight: true,
//SelFgColor: gocui.ColorGreen,
//SelBgColor: gocui.ColorDefault,
OnRender: serviceRender,
OnLineClick: viewLineClickHandler,
OnFocus: func(gui *gui.Gui, view *gui.View) error {
Service = &guilib.View{
Name: serviceViewName,
Title: "Services",
Clickable: true,
OnRender: serviceRender,
OnSelectedLineChange: viewSelectedLineChangeHandler,
Highlight: true,
SelFgColor: gocui.ColorGreen,
OnFocus: func(gui *guilib.Gui, view *guilib.View) error {
if err := onFocusClearSelected(gui, view); err != nil {
return err
}
return nil
},
DimensionFunc: gui.BeneathView(
DimensionFunc: guilib.BeneathView(
namespaceViewName,
reactiveHeight,
migrateTopFunc,
),
Actions: []*guilib.Action{
toNavigation,
nextCyclicView,
previousLine,
nextLine,
newFilterAction(serviceViewName, "services"),
},
}
)
......@@ -17,7 +17,7 @@ import (
const (
OptSeparator = " "
navigationPathJoin = " + "
logsTail = "200"
logsTail = "500"
)
var (
......@@ -29,11 +29,11 @@ var (
functionViews = []string{clusterInfoViewName, namespaceViewName, serviceViewName, deploymentViewName, podViewName}
viewNavigationMap = map[string][]string{
clusterInfoViewName: []string{"Nodes", "Top Nodes"},
namespaceViewName: []string{"Config", "Deployments", "Pods"},
serviceViewName: []string{"Config", "Pods", "Pods Log", "Top Pods"},
deploymentViewName: []string{"Config", "Pods", "Pods Log", "Describe", "Top Pods"},
podViewName: []string{"Log", "Config", "Top", "Describe"},
clusterInfoViewName: {"Nodes", "Top Nodes"},
namespaceViewName: {"Config", "Deployments", "Pods"},
serviceViewName: {"Config", "Pods", "Pods Log", "Top Pods"},
deploymentViewName: {"Config", "Pods", "Pods Log", "Describe", "Top Pods"},
podViewName: {"Log", "Config", "Top", "Describe"},
}
detailRenderMap = map[string]func(gui *guilib.Gui, view *guilib.View) error{
......@@ -59,7 +59,7 @@ var (
)
func notResourceSelected(selectedName string) bool {
if selectedName == "" || selectedName == "NAME" || selectedName == "NAMESPACE" {
if selectedName == "" || selectedName == "NAME" || selectedName == "NAMESPACE" || selectedName == "No" {
return true
}
return false
......@@ -78,6 +78,7 @@ func navigationPath(args ...string) string {
func switchNavigation(index int) string {
Detail.SetOrigin(0, 0)
Detail.Clear()
if index < 0 {
return ""
}
......@@ -172,6 +173,8 @@ func navigationOnClick(gui *guilib.Gui, view *guilib.View) error {
optionIndex := i / 2
log.Logger.Debugf("navigationOnClick - cx %d in selection(%d)[%d, %d]", cx, optionIndex, left, right)
selected = switchNavigation(optionIndex)
view.ReRender()
Detail.ReRender()
break
}
}
......@@ -226,48 +229,37 @@ func topNodesRender(gui *guilib.Gui, view *guilib.View) error {
func namespaceRender(gui *guilib.Gui, view *guilib.View) error {
view.Clear()
streams := newStream()
kubecli.Cli.Get(streams, "namespaces").Run()
renderHighlightSelected(view, streamToString(streams))
kubecli.Cli.Get(viewStreams(view), "namespaces").Run()
return nil
}
func serviceRender(gui *guilib.Gui, view *guilib.View) error {
view.Clear()
streams := newStream()
if kubecli.Cli.Namespace() == "" {
kubecli.Cli.Get(streams, "services").SetFlag("all-namespaces", "true").Run()
renderHighlightSelected(view, streamToString(streams))
kubecli.Cli.Get(viewStreams(view), "services").SetFlag("all-namespaces", "true").Run()
return nil
}
kubecli.Cli.Get(streams, "services").Run()
renderHighlightSelected(view, streamToString(streams))
kubecli.Cli.Get(viewStreams(view), "services").Run()
return nil
}
func deploymentRender(gui *guilib.Gui, view *guilib.View) error {
view.Clear()
streams := newStream()
if kubecli.Cli.Namespace() == "" {
kubecli.Cli.Get(streams, "deployments").SetFlag("all-namespaces", "true").Run()
renderHighlightSelected(view, streamToString(streams))
kubecli.Cli.Get(viewStreams(view), "deployments").SetFlag("all-namespaces", "true").Run()
return nil
}
kubecli.Cli.Get(viewStreams(view), "deployments").Run()
renderHighlightSelected(view, streamToString(streams))
return nil
}
func podRender(gui *guilib.Gui, view *guilib.View) error {
view.Clear()
streams := newStream()
if kubecli.Cli.Namespace() == "" {
kubecli.Cli.Get(streams, "pods").SetFlag("all-namespaces", "true").SetFlag("output", "wide").Run()
renderHighlightSelected(view, streamToString(streams))
kubecli.Cli.Get(viewStreams(view), "pods").SetFlag("all-namespaces", "true").SetFlag("output", "wide").Run()
return nil
}
kubecli.Cli.Get(streams, "pods").SetFlag("output", "wide").Run()
renderHighlightSelected(view, streamToString(streams))
kubecli.Cli.Get(viewStreams(view), "pods").SetFlag("output", "wide").Run()
return nil
}
......@@ -295,17 +287,6 @@ func streamToString(streams genericclioptions.IOStreams) string {
return buf.String()
}
func renderHighlightSelected(view *guilib.View, content string) {
selected, _ := view.State.Get(selectedViewLine)
if selected != nil {
highlightString := selected.(string)
content = strings.Replace(content, highlightString, color.Green.Sprint(highlightString), 1)
fmt.Fprint(view, content)
return
}
fmt.Fprint(view, content)
}
func showPleaseSelected(view *guilib.View, name string) {
fmt.Fprintf(view, "Please select a %s. ", name)
}
......@@ -316,19 +297,13 @@ func namespaceConfigRender(gui *guilib.Gui, view *guilib.View) error {
if err != nil {
return nil
}
selected, _ := namespaceView.State.Get(selectedViewLine)
if selected != nil {
namespace := formatSelectedNamespace(selected.(string))
if namespace == "" {
showPleaseSelected(view, namespaceViewName)
return nil
}
kubecli.Cli.Get(viewStreams(view), "namespaces", namespace).SetFlag("output", "yaml").Run()
namespace := formatSelectedNamespace(namespaceView.SelectedLine)
if notResourceSelected(namespace) {
showPleaseSelected(view, namespaceViewName)
return nil
}
showPleaseSelected(view, namespaceViewName)
kubecli.Cli.Get(viewStreams(view), "namespaces", namespace).SetFlag("output", "yaml").Run()
return nil
}
......@@ -337,16 +312,18 @@ func configRender(gui *guilib.Gui, view *guilib.View) error {
if activeView == nil {
return nil
}
if activeView == Namespace {
return namespaceConfigRender(gui, view)
}
namespaceView, err := gui.GetView(namespaceViewName)
if err != nil {
return nil
}
selectedNamespace, _ := namespaceView.State.Get(selectedViewLine)
selected, _ := activeView.State.Get(selectedViewLine)
if activeView == namespaceView {
return namespaceConfigRender(gui, view)
}
namespace := formatSelectedNamespace(namespaceView.SelectedLine)
selected := activeView.SelectedLine
resource := ""
switch activeView.Name {
case serviceViewName:
......@@ -364,29 +341,29 @@ func configRender(gui *guilib.Gui, view *guilib.View) error {
return nil
}
if selected == nil {
if selected == "" {
showPleaseSelected(view, resource)
return nil
}
if selectedNamespace != nil {
selectedName := formatSelectedName(selected.(string), 0)
if notResourceSelected(selectedName) {
if !notResourceSelected(namespace) {
resourceName := formatResourceName(selected, 0)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
kubecli.Cli.Get(viewStreams(view), resource, selectedName).SetFlag("output", "yaml").Run()
kubecli.Cli.Get(viewStreams(view), resource, resourceName).SetFlag("output", "yaml").Run()
return nil
}
namespace := formatSelectedName(selected.(string), 0)
selectedName := formatSelectedName(selected.(string), 1)
if notResourceSelected(selectedName) {
namespace = formatResourceName(selected, 0)
resourceName := formatResourceName(selected, 1)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
kubecli.Cli.WithNamespace(namespace).Get(viewStreams(view), resource, selectedName).SetFlag("output", "yaml").Run()
kubecli.Cli.WithNamespace(namespace).Get(viewStreams(view), resource, resourceName).SetFlag("output", "yaml").Run()
return nil
}
......@@ -403,8 +380,8 @@ func describeRender(gui *guilib.Gui, view *guilib.View) error {
return nil
}
selectedNamespace, _ := namespaceView.State.Get(selectedViewLine)
selected, _ := activeView.State.Get(selectedViewLine)
namespace := formatSelectedNamespace(namespaceView.SelectedLine)
selected := activeView.SelectedLine
resource := ""
switch activeView.Name {
case deploymentViewName:
......@@ -419,29 +396,29 @@ func describeRender(gui *guilib.Gui, view *guilib.View) error {
return nil
}
if selected == nil {
if notResourceSelected(selected) {
showPleaseSelected(view, resource)
return nil
}
if selectedNamespace != nil {
selectedName := formatSelectedName(selected.(string), 0)
if notResourceSelected(selectedName) {
if !notResourceSelected(namespace) {
resourceName := formatResourceName(selected, 0)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
kubecli.Cli.Describe(viewStreams(view), resource, selectedName).Run()
kubecli.Cli.Describe(viewStreams(view), resource, resourceName).Run()
return nil
}
namespace := formatSelectedName(selected.(string), 0)
selectedName := formatSelectedName(selected.(string), 1)
if notResourceSelected(selectedName) {
namespace = formatResourceName(selected, 0)
resourceName := formatResourceName(selected, 1)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
kubecli.Cli.WithNamespace(namespace).Describe(viewStreams(view), resource, selectedName).Run()
kubecli.Cli.WithNamespace(namespace).Describe(viewStreams(view), resource, resourceName).Run()
view.ReRender()
return nil
}
......@@ -456,43 +433,59 @@ func onFocusClearSelected(gui *guilib.Gui, view *guilib.View) error {
log.Logger.Warningf("onFocusClearSelected - view name %s gui.GetView(\"%s\") error %s", view.Name, functionView, err)
continue
}
functionView.State.Set(selectedViewLine, nil)
if err := functionView.SetOrigin(0, 0); err != nil {
return err
}
if err := functionView.SetCursor(0, 0); err != nil {
return err
}
}
return nil
}
func podLogsRender(gui *guilib.Gui, view *guilib.View) error {
selectedNamespace, _ := Namespace.State.Get(selectedViewLine)
selected, _ := Pod.State.Get(selectedViewLine)
namespaceView, err := gui.GetView(namespaceViewName)
if err != nil {
return err
}
namespace := formatSelectedNamespace(namespaceView.SelectedLine)
podView, err := gui.GetView(podViewName)
if err != nil {
return err
}
selected := podView.SelectedLine
resource := "pod"
if selected == nil {
if notResourceSelected(selected) {
showPleaseSelected(view, resource)
return nil
}
if selectedNamespace != nil {
selectedName := formatSelectedName(selected.(string), 0)
if notResourceSelected(selectedName) {
if !notResourceSelected(namespace) {
resourceName := formatResourceName(selected, 0)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
streams := newStream()
kubecli.Cli.Logs(streams, selectedName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run()
kubecli.Cli.Logs(streams, resourceName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run()
view.Clear()
streamCopyTo(streams, view)
view.ReRender()
return nil
}
namespace := formatSelectedName(selected.(string), 0)
selectedName := formatSelectedName(selected.(string), 1)
if notResourceSelected(selectedName) {
namespace = formatResourceName(selected, 0)
resourceName := formatResourceName(selected, 1)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
streams := newStream()
kubecli.Cli.WithNamespace(namespace).Logs(streams, selectedName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run()
kubecli.Cli.WithNamespace(namespace).Logs(streams, resourceName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run()
streamCopyTo(streams, view)
view.ReRender()
return nil
......@@ -556,8 +549,8 @@ func podsSelectorRenderHelper(cmdFunc func(namespace string, labelsArr []string)
return nil
}
selectedNamespace, _ := namespaceView.State.Get(selectedViewLine)
selected, _ := activeView.State.Get(selectedViewLine)
namespace := formatSelectedNamespace(namespaceView.SelectedLine)
selected := activeView.SelectedLine
var resource string
var jsonPath string
switch activeView.Name {
......@@ -575,28 +568,27 @@ func podsSelectorRenderHelper(cmdFunc func(namespace string, labelsArr []string)
return nil
}
if selected == nil {
if notResourceSelected(selected) {
showPleaseSelected(view, resource)
return nil
}
output := newStream()
var namespace string
if selectedNamespace != nil {
selectedName := formatSelectedName(selected.(string), 0)
if notResourceSelected(selectedName) {
if !notResourceSelected(namespace) {
resourceName := formatResourceName(selected, 0)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
kubecli.Cli.Get(output, resource, selectedName).SetFlag("output", jsonPath).Run()
kubecli.Cli.Get(output, resource, resourceName).SetFlag("output", jsonPath).Run()
} else {
namespace = formatSelectedName(selected.(string), 0)
selectedName := formatSelectedName(selected.(string), 1)
if notResourceSelected(selectedName) {
namespace = formatResourceName(selected, 0)
resourceName := formatResourceName(selected, 1)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
kubecli.Cli.WithNamespace(namespace).Get(output, resource, selectedName).SetFlag("output", jsonPath).Run()
kubecli.Cli.WithNamespace(namespace).Get(output, resource, resourceName).SetFlag("output", jsonPath).Run()
}
labelJson := streamToString(output)
......
......@@ -25,34 +25,44 @@ func podMetricsPlotRender(gui *guilib.Gui, view *guilib.View) error {
}
view.Clear()
selectedNamespace, _ := Namespace.State.Get(selectedViewLine)
selected, _ := Pod.State.Get(selectedViewLine)
namespaceView, err := gui.GetView(namespaceViewName)
if err != nil {
return err
}
namespace := formatSelectedNamespace(namespaceView.SelectedLine)
podView, err := gui.GetView(podViewName)
if err != nil {
return err
}
selected := podView.SelectedLine
resource := "pod"
if selected == nil {
if notResourceSelected(selected) {
showPleaseSelected(view, resource)
return nil
}
var namespace, selectedName string
if selectedNamespace != nil {
selectedName = formatSelectedName(selected.(string), 0)
if selectedName == "" {
var resourceName string
if !notResourceSelected(namespace) {
resourceName = formatResourceName(selected, 0)
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
namespace = kubecli.Cli.Namespace()
} else {
namespace = formatSelectedName(selected.(string), 0)
selectedName = formatSelectedName(selected.(string), 1)
namespace = formatResourceName(selected, 0)
resourceName = formatResourceName(selected, 1)
}
if selectedName == "" {
if notResourceSelected(resourceName) {
showPleaseSelected(view, resource)
return nil
}
metrics, err := kubecli.Cli.GetPodMetrics(namespace, selectedName, false, nil)
metrics, err := kubecli.Cli.GetPodMetrics(namespace, resourceName, false, nil)
if err != nil {
log.Logger.Warningf("podMetricsDataGetter - kubecli.Cli.GetPodMetrics('%s', '%s', false, nil) error %s", namespace, selectedName, err)
log.Logger.Warningf("podMetricsDataGetter - kubecli.Cli.GetPodMetrics('%s', '%s', false, nil) error %s", namespace, resourceName, err)
}
fmt.Fprintln(view)
cpuPlot := getPlot(
......@@ -61,7 +71,7 @@ func podMetricsPlotRender(gui *guilib.Gui, view *guilib.View) error {
cpuPlotStateKey,
"CPU: %0.0fm (%v)",
namespace,
selectedName,
resourceName,
func() []float64 {
data := make([]float64, 0)
if metrics == nil {
......@@ -83,7 +93,7 @@ func podMetricsPlotRender(gui *guilib.Gui, view *guilib.View) error {
memoryPlotStateKey,
"Memory: %0.0fMi (%v)",
namespace,
selectedName,
resourceName,
func() []float64 {
data := make([]float64, 0)
if metrics == nil {
......
......@@ -5,9 +5,7 @@ import (
)
var (
viewHeights = map[string]int{}
resizeView = ""
resizeableViews = []string{namespaceViewName, serviceViewName, deploymentViewName, podViewName}
viewHeights = map[string]int{}
)
func leftSideWidth(maxWidth int) int {
......@@ -38,18 +36,16 @@ func reactiveHeight(gui *gui.Gui, view *gui.View) int {
viewHeights[podViewName] = space / tallPanels
viewHeights[optionViewName] = 1
resizeView := namespaceViewName
currentView := gui.CurrentView()
if currentView != nil {
for _, viewName := range resizeableViews {
if currentView.Name == viewName {
resizeView = viewName
break
}
}
viewHeights[resizeView] += space % tallPanels
if currentView != nil && currentView.Name != clusterInfoViewName && currentView.Name != navigationViewName && currentView.Name != detailViewName {
resizeView = currentView.Name
} else if gui.PeekPreviousView() != "" && gui.PeekPreviousView() != clusterInfoViewName {
resizeView = gui.PeekPreviousView()
}
viewHeights[resizeView] += space % tallPanels
height := viewHeights[view.Name]
return height
}
......
......@@ -2,6 +2,7 @@ package config
import "github.com/jroimartin/gocui"
// GuiConfig GuiConfig
type GuiConfig struct {
Highlight bool
Cursor bool
......
......@@ -6,6 +6,7 @@ import (
)
var (
// Quit Quit
Quit = &Action{
Name: "Quit",
Key: gocui.KeyCtrlC,
......@@ -17,22 +18,22 @@ var (
Mod: gocui.ModNone,
}
// ClickView ClickView
ClickView = &Action{
Name: "clickView",
Key: gocui.MouseLeft,
Handler: ViewClickHandler,
Mod: gocui.ModNone,
ReRender: true,
Name: "clickView",
Key: gocui.MouseLeft,
Handler: ViewClickHandler,
Mod: gocui.ModNone,
}
)
type Action struct {
Name string
Key interface{}
Keys []interface{}
ReRender bool
Handler func(gui *Gui) func(*gocui.Gui, *gocui.View) error
Mod gocui.Modifier
Name string
Key interface{}
Keys []interface{}
ReRenderAllView bool
Handler func(gui *Gui) func(*gocui.Gui, *gocui.View) error
Mod gocui.Modifier
}
type ActionHandler func(gui *Gui) func(*gocui.Gui, *gocui.View) error
......
package gui
// BeneathView BeneathView
func BeneathView(aboveViewName string, heightFunc func(*Gui, *View) int, marginTopFunc func(*Gui, *View) int) func(gui *Gui, view *View) (int, int, int, int) {
return func(gui *Gui, view *View) (int, int, int, int) {
aboveX0, _, aboveX1, aboveY1, err := gui.g.ViewPosition(aboveViewName)
......
package gui
import "github.com/jroimartin/gocui"
// NewViewEditor NewViewEditor
func NewViewEditor(gui *Gui, view *View) gocui.Editor {
return gocui.EditorFunc(ViewEditorFunc(gui, view))
}
// ViewEditorFunc ViewEditorFunc
func ViewEditorFunc(gui *Gui, view *View) func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
gocui.DefaultEditor.Edit(v, key, ch, mod)
if view.OnEditedChange != nil {
view.OnEditedChange(gui, view, key, ch, mod)
}
}
}
......@@ -3,5 +3,6 @@ package gui
import "errors"
var (
// ErrNotEnoughSpace ErrNotEnoughSpace
ErrNotEnoughSpace = errors.New("not enough space")
)
\ No newline at end of file
)
......@@ -6,30 +6,35 @@ import (
"github.com/jroimartin/gocui"
)
// Gui Gui
type Gui struct {
State State
reRendered bool
OnRender func(gui *Gui) error
OnRenderOptions func(gui *Gui) error
Config config.GuiConfig
// History of focused views name.
previousViews TowHeadQueue
previousViewsLimit int
g *gocui.Gui
views []*View
config config.GuiConfig
g *gocui.Gui
views []*View
preHeight int
preWidth int
Actions []*Action
}
// NewGui NewGui
func NewGui(config config.GuiConfig, views ...*View) *Gui {
gui := &Gui{
State: NewStateMap(),
previousViews: NewQueue(),
previousViewsLimit: 20,
Config: config,
}
gui.views = make([]*View, 0)
g, err := gocui.NewGui(gocui.OutputNormal)
......@@ -39,7 +44,7 @@ func NewGui(config config.GuiConfig, views ...*View) *Gui {
}
gui.g = g
gui.Configure(config)
gui.Configure()
gui.g.SetManagerFunc(gui.layout)
......@@ -53,6 +58,7 @@ func NewGui(config config.GuiConfig, views ...*View) *Gui {
return gui
}
// ReRender ReRender
func (gui *Gui) ReRender() {
gui.reRendered = false
for _, view := range gui.views {
......@@ -72,6 +78,10 @@ func (gui *Gui) layout(*gocui.Gui) error {
return err
}
for _, view := range gui.views {
if err := gui.updateSelectedViewLine(view); err != nil {
return err
}
err := gui.RenderView(view)
if err == nil {
continue
......@@ -98,39 +108,77 @@ func (gui *Gui) layout(*gocui.Gui) error {
return err
}
if err := gui.setTopViews(); err != nil {
return err
}
return nil
}
func (gui *Gui) updateSelectedViewLine(view *View) error {
if !view.Rendered() {
return nil
}
_, cy := view.Cursor()
selectedLine, _ := view.Line(cy)
if selectedLine != view.SelectedLine {
view.SelectedLine = selectedLine
if view.OnSelectedLineChange != nil {
if err := view.OnSelectedLineChange(gui, view, selectedLine); err != nil {
return err
}
}
}
return nil
}
func (gui *Gui) setTopViews() error {
for _, view := range gui.views {
if view.AlwaysOnTop {
if _, err := gui.SetViewOnTop(view.Name); err != nil {
return err
}
}
}
return nil
}
func (gui *Gui) Configure(config config.GuiConfig) {
gui.g.Highlight = config.Highlight
gui.g.Cursor = config.Cursor
gui.g.SelFgColor = config.SelFgColor
gui.g.SelBgColor = config.SelBgColor
gui.g.FgColor = config.FgColor
gui.g.BgColor = config.BgColor
gui.g.Mouse = config.Mouse
gui.g.InputEsc = config.InputEsc
gui.config = config
// Configure Configure
func (gui *Gui) Configure() {
gui.g.Highlight = gui.Config.Highlight
gui.g.Cursor = gui.Config.Cursor
gui.g.SelFgColor = gui.Config.SelFgColor
gui.g.SelBgColor = gui.Config.SelBgColor
gui.g.FgColor = gui.Config.FgColor
gui.g.BgColor = gui.Config.BgColor
gui.g.Mouse = gui.Config.Mouse
gui.g.InputEsc = gui.Config.InputEsc
}
// Size Size
func (gui *Gui) Size() (int, int) {
return gui.g.Size()
}
// MaxWidth MaxWidth
func (gui *Gui) MaxWidth() int {
maxWidth, _ := gui.g.Size()
return maxWidth
}
// MaxHeight MaxHeight
func (gui *Gui) MaxHeight() int {
_, maxHeight := gui.g.Size()
return maxHeight
}
// GetViews GetViews
func (gui *Gui) GetViews() []*View {
return gui.views
}
// SetKeybinding SetKeybinding
func (gui *Gui) SetKeybinding(viewName string, key interface{}, mod gocui.Modifier, handler func(*gocui.Gui, *gocui.View) error) {
if err := gui.g.SetKeybinding(
viewName,
......@@ -142,9 +190,10 @@ func (gui *Gui) SetKeybinding(viewName string, key interface{}, mod gocui.Modifi
}
}
// BindAction BindAction
func (gui *Gui) BindAction(viewName string, action *Action) {
var handler func(g *gocui.Gui, v *gocui.View) error
if action.ReRender {
if action.ReRenderAllView {
handler = func(g *gocui.Gui, v *gocui.View) error {
if err := action.Handler(gui)(g, v); err != nil {
return err
......@@ -175,6 +224,7 @@ func (gui *Gui) BindAction(viewName string, action *Action) {
}
}
// ViewDimensionValidated ViewDimensionValidated
func (gui *Gui) ViewDimensionValidated(x0, y0, x1, y1 int) bool {
if x0 >= x1 || y0 >= y1 {
return false
......@@ -183,11 +233,23 @@ func (gui *Gui) ViewDimensionValidated(x0, y0, x1, y1 int) bool {
return true
}
// Run Run
func (gui *Gui) Run() {
if gui.Actions != nil {
for _, act := range gui.Actions {
gui.BindAction("", act)
}
}
for _, view := range gui.views {
if view.Clickable {
gui.BindAction(view.Name, ClickView)
}
if view.Actions != nil {
for _, act := range view.Actions {
gui.BindAction(view.Name, act)
}
}
}
if err := gui.g.MainLoop(); err != nil && err != gocui.ErrQuit {
......@@ -195,10 +257,12 @@ func (gui *Gui) Run() {
}
}
// Close Close
func (gui *Gui) Close() {
gui.g.Close()
}
// GetView GetView
func (gui *Gui) GetView(name string) (*View, error) {
if err := gui.ViewExisted(name); err != nil {
return nil, err
......@@ -207,6 +271,7 @@ func (gui *Gui) GetView(name string) (*View, error) {
return gui.getView(name), nil
}
// RenderView RenderView
func (gui *Gui) RenderView(view *View) error {
x0, y0, x1, y1 := view.GetDimensions()
if !gui.ViewDimensionValidated(x0, y0, x1, y1) {
......@@ -224,6 +289,7 @@ func (gui *Gui) unRenderNotEnoughSpaceView() error {
return nil
}
// Clear Clear
func (gui *Gui) Clear() error {
if err := gui.unRenderNotEnoughSpaceView(); err != nil {
return err
......@@ -240,6 +306,7 @@ func (gui *Gui) renderNotEnoughSpaceView() error {
return gui.renderView(NotEnoughSpace, x0, y0, x1, y1)
}
// SetView SetView
func (gui *Gui) SetView(view *View, x0, y0, x1, y1 int) (*View, error) {
if v, err := gui.g.SetView(
view.Name,
......@@ -280,13 +347,15 @@ func (gui *Gui) renderView(view *View, x0, y0, x1, y1 int) error {
return nil
}
// ViewColors ViewColors
func (gui *Gui) ViewColors(view *View) (gocui.Attribute, gocui.Attribute) {
if gui.config.Highlight && view == gui.CurrentView() {
return gui.config.SelFgColor, gui.config.SelBgColor
if gui.Config.Highlight && view == gui.CurrentView() {
return gui.Config.SelFgColor, gui.Config.SelBgColor
}
return gui.config.FgColor, gui.config.BgColor
return gui.Config.FgColor, gui.Config.BgColor
}
// CurrentView CurrentView
func (gui *Gui) CurrentView() *View {
v := gui.g.CurrentView()
if v == nil {
......@@ -295,11 +364,33 @@ func (gui *Gui) CurrentView() *View {
return gui.getView(v.Name())
}
func (gui *Gui) AddView(view *View) {
// AddView AddView
func (gui *Gui) AddView(view *View) error {
// Todo: Check if view existed
gui.views = append(gui.views, view)
view.gui = gui
err := gui.RenderView(view)
if err == ErrNotEnoughSpace {
if err := gui.renderNotEnoughSpaceView(); err != nil {
return err
}
return nil
}
if view.Clickable {
gui.BindAction(view.Name, ClickView)
}
if view.Actions != nil {
for _, act := range view.Actions {
gui.BindAction(view.Name, act)
}
}
return nil
}
// DeleteView DeleteView
func (gui *Gui) DeleteView(name string) error {
if err := gui.ViewExisted(name); err != nil {
return err
......@@ -315,9 +406,12 @@ func (gui *Gui) DeleteView(name string) error {
}
}
gui.g.DeleteKeybindings(name)
return nil
}
// ViewExisted ViewExisted
func (gui *Gui) ViewExisted(name string) error {
_, err := gui.g.View(name)
if err != nil {
......@@ -326,6 +420,7 @@ func (gui *Gui) ViewExisted(name string) error {
return nil
}
// RenderString RenderString
func (gui *Gui) RenderString(viewName, s string) error {
gui.Update(func(g *gocui.Gui) error {
view, err := gui.GetView(viewName)
......@@ -350,10 +445,12 @@ func (gui *Gui) RenderString(viewName, s string) error {
return nil
}
// Update Update
func (gui *Gui) Update(f func(*gocui.Gui) error) {
gui.g.Update(f)
}
// SetCurrentView SetCurrentView
func (gui *Gui) SetCurrentView(name string) (*View, error) {
if _, err := gui.g.SetCurrentView(name); err != nil {
return nil, err
......@@ -362,6 +459,7 @@ func (gui *Gui) SetCurrentView(name string) (*View, error) {
return view, nil
}
// SetViewOnTop SetViewOnTop
func (gui *Gui) SetViewOnTop(name string) (*View, error) {
if _, err := gui.g.SetViewOnTop(name); err != nil {
return nil, err
......@@ -452,10 +550,12 @@ func (gui *Gui) focusView(name string) error {
return nil
}
// HasPreviousView HasPreviousView
func (gui *Gui) HasPreviousView() bool {
return !gui.previousViews.IsEmpty()
}
// ReturnPreviousView ReturnPreviousView
func (gui *Gui) ReturnPreviousView() error {
previousViewName := gui.popPreviousView()
previousView, err := gui.GetView(previousViewName)
......@@ -467,7 +567,7 @@ func (gui *Gui) ReturnPreviousView() error {
return err
}
log.Logger.Debugf("ReturnPreviousView - gui.focusView(%s)", previousView.Name)
return gui.focusView(previousView.Name)
return gui.FocusView(previousView.Name, false)
}
func (gui *Gui) renderOptions() error {
......@@ -486,6 +586,20 @@ func (gui *Gui) renderOptions() error {
return nil
}
// SetRune SetRune
func (gui *Gui) SetRune(x, y int, ch rune, fgColor, bgColor gocui.Attribute) error {
return gui.g.SetRune(x, y, ch, fgColor, bgColor)
}
// ReRenderViews ReRenderViews
func (gui *Gui) ReRenderViews(viewNames ...string) {
for _, name := range viewNames {
view, err := gui.GetView(name)
if err != nil {
log.Logger.Warningf("ReRenderViews - view '%s' error %s", name, err)
continue
}
view.ReRender()
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment