refactor side list panel some more

This commit is contained in:
Jesse Duffield 2022-10-31 12:25:45 -07:00
parent 02a03ed18d
commit d6ad538426
10 changed files with 104 additions and 96 deletions

View file

@ -100,18 +100,6 @@ func getBindingSections(mApp *app.App) []*bindingSection {
bindingSections = addBinding(titleMap[viewName], bindingSections, binding) 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 return bindingSections
} }

View file

@ -32,8 +32,8 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
return &panels.SideListPanel[*commands.Container]{ return &panels.SideListPanel[*commands.Container]{
ContextState: &panels.ContextState[*commands.Container]{ ContextState: &panels.ContextState[*commands.Container]{
GetContexts: func() []panels.ContextConfig[*commands.Container] { GetMainTabs: func() []panels.MainTab[*commands.Container] {
return []panels.ContextConfig[*commands.Container]{ return []panels.MainTab[*commands.Container]{
{ {
Key: "logs", Key: "logs",
Title: gui.Tr.LogsTitle, 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 return "containers-" + container.ID + "-" + container.Container.State
}, },
}, },

View file

@ -22,8 +22,8 @@ func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] {
return &panels.SideListPanel[*commands.Image]{ return &panels.SideListPanel[*commands.Image]{
ContextState: &panels.ContextState[*commands.Image]{ ContextState: &panels.ContextState[*commands.Image]{
GetContexts: func() []panels.ContextConfig[*commands.Image] { GetMainTabs: func() []panels.MainTab[*commands.Image] {
return []panels.ContextConfig[*commands.Image]{ return []panels.MainTab[*commands.Image]{
{ {
Key: "config", Key: "config",
Title: gui.Tr.ConfigTitle, 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 return "images-" + image.ID
}, },
}, },

View file

@ -532,14 +532,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: panel.GetView().Name(), ViewName: panel.GetView().Name(),
Key: '[', Key: '[',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: wrappedHandler(panel.HandlePrevContext), Handler: wrappedHandler(panel.HandlePrevMainTab),
Description: gui.Tr.PreviousContext, Description: gui.Tr.PreviousContext,
}, },
&Binding{ &Binding{
ViewName: panel.GetView().Name(), ViewName: panel.GetView().Name(),
Key: ']', Key: ']',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: wrappedHandler(panel.HandleNextContext), Handler: wrappedHandler(panel.HandleNextMainTab),
Description: gui.Tr.NextContext, Description: gui.Tr.NextContext,
}, },
) )

View file

@ -89,7 +89,7 @@ func (gui *Gui) onMainTabClick(tabIndex int) error {
return nil return nil
} }
currentSidePanel.SetContextIndex(tabIndex) currentSidePanel.SetMainTabIndex(tabIndex)
return currentSidePanel.HandleSelect() return currentSidePanel.HandleSelect()
} }

View file

@ -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
}

View file

@ -11,7 +11,7 @@ import (
) )
type ISideListPanel interface { type ISideListPanel interface {
SetContextIndex(int) SetMainTabIndex(int)
HandleSelect() error HandleSelect() error
GetView() *gocui.View GetView() *gocui.View
Refocus() Refocus()
@ -21,8 +21,8 @@ type ISideListPanel interface {
HandleNextLine() error HandleNextLine() error
HandlePrevLine() error HandlePrevLine() error
HandleClick() error HandleClick() error
HandlePrevContext() error HandlePrevMainTab() error
HandleNextContext() error HandleNextMainTab() error
} }
// list panel at the side of the screen that renders content to the main panel // 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 OnClick func(T) error
// returns the columns that we render to the view in a table formats
GetDisplayStrings func(T) []string GetDisplayStrings func(T) []string
// function to be called after re-rendering list. Can be nil // function to be called after re-rendering list. Can be nil
@ -57,25 +58,11 @@ type SideListPanel[T comparable] struct {
var _ ISideListPanel = &SideListPanel[int]{} 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 { type IGui interface {
HandleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func() error) error HandleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func() error) error
RenderStringMain(message string) error RenderStringMain(message string) error
FocusY(selectedLine int, itemCount int, view *gocui.View) FocusY(selectedLine int, itemCount int, view *gocui.View)
ShouldRefresh(key string) bool ShouldRefresh(contextKey string) bool
GetMainView() *gocui.View GetMainView() *gocui.View
// TODO: replace with IsCurrentView() bool // TODO: replace with IsCurrentView() bool
CurrentView() *gocui.View CurrentView() *gocui.View
@ -137,24 +124,10 @@ func (self *SideListPanel[T]) renderContext(item T) error {
} }
mainView := self.Gui.GetMainView() mainView := self.Gui.GetMainView()
mainView.Tabs = self.ContextState.GetContextTitles() mainView.Tabs = self.ContextState.GetMainTabTitles()
mainView.TabIndex = self.ContextState.contextIdx mainView.TabIndex = self.ContextState.mainTabIdx
return self.ContextState.GetCurrentContext().Render(item) return self.ContextState.GetCurrentMainTab().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]
} }
func (self *SideListPanel[T]) GetSelectedItem() (T, error) { func (self *SideListPanel[T]) GetSelectedItem() (T, error) {
@ -181,42 +154,22 @@ func (self *SideListPanel[T]) HandlePrevLine() error {
return self.HandleSelect() return self.HandleSelect()
} }
func (self *ContextState[T]) HandleNextContext() { func (self *SideListPanel[T]) HandleNextMainTab() error {
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 {
if self.ContextState == nil { if self.ContextState == nil {
return nil return nil
} }
self.ContextState.HandleNextContext() self.ContextState.HandleNextMainTab()
return self.HandleSelect() return self.HandleSelect()
} }
func (self *SideListPanel[T]) HandlePrevContext() error { func (self *SideListPanel[T]) HandlePrevMainTab() error {
if self.ContextState == nil { if self.ContextState == nil {
return nil return nil
} }
self.ContextState.HandlePrevContext() self.ContextState.HandlePrevMainTab()
return self.HandleSelect() return self.HandleSelect()
} }
@ -289,16 +242,12 @@ func (self *SideListPanel[T]) RerenderList() error {
return nil return nil
} }
func (self *SideListPanel[T]) SetContextIndex(index int) { func (self *SideListPanel[T]) SetMainTabIndex(index int) {
if self.ContextState == nil { if self.ContextState == nil {
return return
} }
self.ContextState.SetContextIndex(index) self.ContextState.SetMainTabIndex(index)
}
func (self *ContextState[T]) SetContextIndex(index int) {
self.contextIdx = index
} }
func (self *SideListPanel[T]) IsFilterDisabled() bool { func (self *SideListPanel[T]) IsFilterDisabled() bool {

View file

@ -20,9 +20,9 @@ import (
func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] { func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
return &panels.SideListPanel[*commands.Project]{ return &panels.SideListPanel[*commands.Project]{
ContextState: &panels.ContextState[*commands.Project]{ ContextState: &panels.ContextState[*commands.Project]{
GetContexts: func() []panels.ContextConfig[*commands.Project] { GetMainTabs: func() []panels.MainTab[*commands.Project] {
if gui.DockerCommand.InDockerComposeProject { if gui.DockerCommand.InDockerComposeProject {
return []panels.ContextConfig[*commands.Project]{ return []panels.MainTab[*commands.Project]{
{ {
Key: "logs", Key: "logs",
Title: gui.Tr.LogsTitle, 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", Key: "credits",
Title: gui.Tr.CreditsTitle, 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 return "projects-" + project.Name
}, },
}, },

View file

@ -18,8 +18,8 @@ import (
func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] { func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
return &panels.SideListPanel[*commands.Service]{ return &panels.SideListPanel[*commands.Service]{
ContextState: &panels.ContextState[*commands.Service]{ ContextState: &panels.ContextState[*commands.Service]{
GetContexts: func() []panels.ContextConfig[*commands.Service] { GetMainTabs: func() []panels.MainTab[*commands.Service] {
return []panels.ContextConfig[*commands.Service]{ return []panels.MainTab[*commands.Service]{
{ {
Key: "logs", Key: "logs",
Title: gui.Tr.LogsTitle, 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 { if service.Container == nil {
return "services-" + service.ID return "services-" + service.ID
} }

View file

@ -17,8 +17,8 @@ import (
func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] { func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] {
return &panels.SideListPanel[*commands.Volume]{ return &panels.SideListPanel[*commands.Volume]{
ContextState: &panels.ContextState[*commands.Volume]{ ContextState: &panels.ContextState[*commands.Volume]{
GetContexts: func() []panels.ContextConfig[*commands.Volume] { GetMainTabs: func() []panels.MainTab[*commands.Volume] {
return []panels.ContextConfig[*commands.Volume]{ return []panels.MainTab[*commands.Volume]{
{ {
Key: "config", Key: "config",
Title: gui.Tr.ConfigTitle, 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 return "volumes-" + volume.Name
}, },
}, },