diff --git a/pkg/cheatsheet/generate.go b/pkg/cheatsheet/generate.go index 8c25da30..1e17cd92 100644 --- a/pkg/cheatsheet/generate.go +++ b/pkg/cheatsheet/generate.go @@ -100,18 +100,6 @@ func getBindingSections(mApp *app.App) []*bindingSection { bindingSections = addBinding(titleMap[viewName], bindingSections, binding) } - // for view, contexts := range mApp.Gui.GetContextMap() { - // for contextName, contextBindings := range contexts { - // translatedView := localisedTitle(mApp, view) - // translatedContextName := localisedTitle(mApp, contextName) - // title := fmt.Sprintf("%s (%s)", translatedView, translatedContextName) - - // for _, binding := range contextBindings { - // bindingSections = addBinding(title, bindingSections, binding) - // } - // } - // } - return bindingSections } diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index 7bcf5b1e..1ad61db2 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -32,8 +32,8 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] return &panels.SideListPanel[*commands.Container]{ ContextState: &panels.ContextState[*commands.Container]{ - GetContexts: func() []panels.ContextConfig[*commands.Container] { - return []panels.ContextConfig[*commands.Container]{ + GetMainTabs: func() []panels.MainTab[*commands.Container] { + return []panels.MainTab[*commands.Container]{ { Key: "logs", Title: gui.Tr.LogsTitle, @@ -61,7 +61,12 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] }, } }, - GetContextCacheKey: func(container *commands.Container) string { + GetItemContextCacheKey: func(container *commands.Container) string { + // Including the container state in the cache key so that if the container + // restarts we re-read the logs. In the past we've had some glitchiness + // where a container restarts but the new logs don't get read. + // Note that this might be jarring if we have a lot of logs and the container + // restarts a lot, so let's keep an eye on it. return "containers-" + container.ID + "-" + container.Container.State }, }, diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go index 1c503d22..82d9222f 100644 --- a/pkg/gui/images_panel.go +++ b/pkg/gui/images_panel.go @@ -22,8 +22,8 @@ func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] { return &panels.SideListPanel[*commands.Image]{ ContextState: &panels.ContextState[*commands.Image]{ - GetContexts: func() []panels.ContextConfig[*commands.Image] { - return []panels.ContextConfig[*commands.Image]{ + GetMainTabs: func() []panels.MainTab[*commands.Image] { + return []panels.MainTab[*commands.Image]{ { Key: "config", Title: gui.Tr.ConfigTitle, @@ -33,7 +33,7 @@ func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] { }, } }, - GetContextCacheKey: func(image *commands.Image) string { + GetItemContextCacheKey: func(image *commands.Image) string { return "images-" + image.ID }, }, diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 00934a6a..908ce3d4 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -532,14 +532,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { ViewName: panel.GetView().Name(), Key: '[', Modifier: gocui.ModNone, - Handler: wrappedHandler(panel.HandlePrevContext), + Handler: wrappedHandler(panel.HandlePrevMainTab), Description: gui.Tr.PreviousContext, }, &Binding{ ViewName: panel.GetView().Name(), Key: ']', Modifier: gocui.ModNone, - Handler: wrappedHandler(panel.HandleNextContext), + Handler: wrappedHandler(panel.HandleNextMainTab), Description: gui.Tr.NextContext, }, ) diff --git a/pkg/gui/main_panel.go b/pkg/gui/main_panel.go index 5629c6d3..a653068c 100644 --- a/pkg/gui/main_panel.go +++ b/pkg/gui/main_panel.go @@ -89,7 +89,7 @@ func (gui *Gui) onMainTabClick(tabIndex int) error { return nil } - currentSidePanel.SetContextIndex(tabIndex) + currentSidePanel.SetMainTabIndex(tabIndex) return currentSidePanel.HandleSelect() } diff --git a/pkg/gui/panels/context_state.go b/pkg/gui/panels/context_state.go new file mode 100644 index 00000000..2cdfeec9 --- /dev/null +++ b/pkg/gui/panels/context_state.go @@ -0,0 +1,66 @@ +package panels + +import "github.com/samber/lo" + +// A 'context' generally corresponds to an item and the tab in the main panel that we're +// displaying. So if we switch to a new item, or change the tab in the panel panel +// for the current item, we end up with a new context. When we have a new context, +// we render new content to the main panel. +type ContextState[T any] struct { + // index of the currently selected tab in the main view. + mainTabIdx int + // this function returns the tabs that we can display for an item (the tabs + // are shown on the main view) + GetMainTabs func() []MainTab[T] + // This tells us whether we need to re-render to the main panel for a given item. + // This should include the item's ID and if you want to invalidate the cache for + // some other reason, you can add that to the key as well (e.g. the container's state). + GetItemContextCacheKey func(item T) string +} + +type MainTab[T any] struct { + // key used as part of the context cache key + Key string + // title of the tab, rendered in the main view + Title string + // function to render the content of the tab + Render func(item T) error +} + +func (self *ContextState[T]) GetMainTabTitles() []string { + return lo.Map(self.GetMainTabs(), func(tab MainTab[T], _ int) string { + return tab.Title + }) +} + +func (self *ContextState[T]) GetCurrentContextKey(item T) string { + return self.GetItemContextCacheKey(item) + "-" + self.GetCurrentMainTab().Key +} + +func (self *ContextState[T]) GetCurrentMainTab() MainTab[T] { + return self.GetMainTabs()[self.mainTabIdx] +} + +func (self *ContextState[T]) HandleNextMainTab() { + tabs := self.GetMainTabs() + + if len(tabs) == 0 { + return + } + + self.mainTabIdx = (self.mainTabIdx + 1) % len(tabs) +} + +func (self *ContextState[T]) HandlePrevMainTab() { + tabs := self.GetMainTabs() + + if len(tabs) == 0 { + return + } + + self.mainTabIdx = (self.mainTabIdx - 1 + len(tabs)) % len(tabs) +} + +func (self *ContextState[T]) SetMainTabIndex(index int) { + self.mainTabIdx = index +} diff --git a/pkg/gui/panels/side_list_panel.go b/pkg/gui/panels/side_list_panel.go index f6e329ca..3652eeec 100644 --- a/pkg/gui/panels/side_list_panel.go +++ b/pkg/gui/panels/side_list_panel.go @@ -11,7 +11,7 @@ import ( ) type ISideListPanel interface { - SetContextIndex(int) + SetMainTabIndex(int) HandleSelect() error GetView() *gocui.View Refocus() @@ -21,8 +21,8 @@ type ISideListPanel interface { HandleNextLine() error HandlePrevLine() error HandleClick() error - HandlePrevContext() error - HandleNextContext() error + HandlePrevMainTab() error + HandleNextMainTab() error } // list panel at the side of the screen that renders content to the main panel @@ -43,6 +43,7 @@ type SideListPanel[T comparable] struct { OnClick func(T) error + // returns the columns that we render to the view in a table formats GetDisplayStrings func(T) []string // function to be called after re-rendering list. Can be nil @@ -57,25 +58,11 @@ type SideListPanel[T comparable] struct { var _ ISideListPanel = &SideListPanel[int]{} -type ContextState[T any] struct { - contextIdx int - // contexts []ContextConfig[T] - GetContexts func() []ContextConfig[T] - // this tells us whether we need to re-render to the main panel - GetContextCacheKey func(item T) string -} - -type ContextConfig[T any] struct { - Key string - Title string - Render func(item T) error -} - type IGui interface { HandleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func() error) error RenderStringMain(message string) error FocusY(selectedLine int, itemCount int, view *gocui.View) - ShouldRefresh(key string) bool + ShouldRefresh(contextKey string) bool GetMainView() *gocui.View // TODO: replace with IsCurrentView() bool CurrentView() *gocui.View @@ -137,24 +124,10 @@ func (self *SideListPanel[T]) renderContext(item T) error { } mainView := self.Gui.GetMainView() - mainView.Tabs = self.ContextState.GetContextTitles() - mainView.TabIndex = self.ContextState.contextIdx + mainView.Tabs = self.ContextState.GetMainTabTitles() + mainView.TabIndex = self.ContextState.mainTabIdx - return self.ContextState.GetCurrentContext().Render(item) -} - -func (self *ContextState[T]) GetContextTitles() []string { - return lo.Map(self.GetContexts(), func(context ContextConfig[T], _ int) string { - return context.Title - }) -} - -func (self *ContextState[T]) GetCurrentContextKey(item T) string { - return self.GetContextCacheKey(item) + "-" + self.GetCurrentContext().Key -} - -func (self *ContextState[T]) GetCurrentContext() ContextConfig[T] { - return self.GetContexts()[self.contextIdx] + return self.ContextState.GetCurrentMainTab().Render(item) } func (self *SideListPanel[T]) GetSelectedItem() (T, error) { @@ -181,42 +154,22 @@ func (self *SideListPanel[T]) HandlePrevLine() error { return self.HandleSelect() } -func (self *ContextState[T]) HandleNextContext() { - contexts := self.GetContexts() - - if len(contexts) == 0 { - return - } - - self.contextIdx = (self.contextIdx + 1) % len(contexts) -} - -func (self *ContextState[T]) HandlePrevContext() { - contexts := self.GetContexts() - - if len(contexts) == 0 { - return - } - - self.contextIdx = (self.contextIdx - 1 + len(contexts)) % len(contexts) -} - -func (self *SideListPanel[T]) HandleNextContext() error { +func (self *SideListPanel[T]) HandleNextMainTab() error { if self.ContextState == nil { return nil } - self.ContextState.HandleNextContext() + self.ContextState.HandleNextMainTab() return self.HandleSelect() } -func (self *SideListPanel[T]) HandlePrevContext() error { +func (self *SideListPanel[T]) HandlePrevMainTab() error { if self.ContextState == nil { return nil } - self.ContextState.HandlePrevContext() + self.ContextState.HandlePrevMainTab() return self.HandleSelect() } @@ -289,16 +242,12 @@ func (self *SideListPanel[T]) RerenderList() error { return nil } -func (self *SideListPanel[T]) SetContextIndex(index int) { +func (self *SideListPanel[T]) SetMainTabIndex(index int) { if self.ContextState == nil { return } - self.ContextState.SetContextIndex(index) -} - -func (self *ContextState[T]) SetContextIndex(index int) { - self.contextIdx = index + self.ContextState.SetMainTabIndex(index) } func (self *SideListPanel[T]) IsFilterDisabled() bool { diff --git a/pkg/gui/project_panel.go b/pkg/gui/project_panel.go index b7c9138c..c40edc85 100644 --- a/pkg/gui/project_panel.go +++ b/pkg/gui/project_panel.go @@ -20,9 +20,9 @@ import ( func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] { return &panels.SideListPanel[*commands.Project]{ ContextState: &panels.ContextState[*commands.Project]{ - GetContexts: func() []panels.ContextConfig[*commands.Project] { + GetMainTabs: func() []panels.MainTab[*commands.Project] { if gui.DockerCommand.InDockerComposeProject { - return []panels.ContextConfig[*commands.Project]{ + return []panels.MainTab[*commands.Project]{ { Key: "logs", Title: gui.Tr.LogsTitle, @@ -41,7 +41,7 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] { } } - return []panels.ContextConfig[*commands.Project]{ + return []panels.MainTab[*commands.Project]{ { Key: "credits", Title: gui.Tr.CreditsTitle, @@ -49,7 +49,7 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] { }, } }, - GetContextCacheKey: func(project *commands.Project) string { + GetItemContextCacheKey: func(project *commands.Project) string { return "projects-" + project.Name }, }, diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go index d89d2eb5..3fdad1f3 100644 --- a/pkg/gui/services_panel.go +++ b/pkg/gui/services_panel.go @@ -18,8 +18,8 @@ import ( func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] { return &panels.SideListPanel[*commands.Service]{ ContextState: &panels.ContextState[*commands.Service]{ - GetContexts: func() []panels.ContextConfig[*commands.Service] { - return []panels.ContextConfig[*commands.Service]{ + GetMainTabs: func() []panels.MainTab[*commands.Service] { + return []panels.MainTab[*commands.Service]{ { Key: "logs", Title: gui.Tr.LogsTitle, @@ -47,7 +47,7 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] { }, } }, - GetContextCacheKey: func(service *commands.Service) string { + GetItemContextCacheKey: func(service *commands.Service) string { if service.Container == nil { return "services-" + service.ID } diff --git a/pkg/gui/volumes_panel.go b/pkg/gui/volumes_panel.go index fb02383f..123f007c 100644 --- a/pkg/gui/volumes_panel.go +++ b/pkg/gui/volumes_panel.go @@ -17,8 +17,8 @@ import ( func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] { return &panels.SideListPanel[*commands.Volume]{ ContextState: &panels.ContextState[*commands.Volume]{ - GetContexts: func() []panels.ContextConfig[*commands.Volume] { - return []panels.ContextConfig[*commands.Volume]{ + GetMainTabs: func() []panels.MainTab[*commands.Volume] { + return []panels.MainTab[*commands.Volume]{ { Key: "config", Title: gui.Tr.ConfigTitle, @@ -26,7 +26,7 @@ func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] { }, } }, - GetContextCacheKey: func(volume *commands.Volume) string { + GetItemContextCacheKey: func(volume *commands.Volume) string { return "volumes-" + volume.Name }, },