mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
move context stuff into its own struct
This commit is contained in:
parent
c6d424e4a4
commit
5d498c796a
9 changed files with 281 additions and 257 deletions
|
|
@ -22,58 +22,58 @@ func (gui *Gui) getContainersPanel() *SideListPanel[*commands.Container] {
|
|||
return true
|
||||
}
|
||||
|
||||
return !lo.SomeBy(gui.Panels.Services.list.GetAllItems(), func(service *commands.Service) bool {
|
||||
return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
|
||||
return service.Name == container.ServiceName
|
||||
})
|
||||
}
|
||||
|
||||
return &SideListPanel[*commands.Container]{
|
||||
contextKeyPrefix: "containers",
|
||||
ContextState: &ContextState[*commands.Container]{
|
||||
GetContexts: func() []ContextConfig[*commands.Container] {
|
||||
return []ContextConfig[*commands.Container]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderContainerLogsToMain,
|
||||
},
|
||||
{
|
||||
key: "stats",
|
||||
title: gui.Tr.StatsTitle,
|
||||
render: gui.renderContainerStats,
|
||||
},
|
||||
{
|
||||
key: "env",
|
||||
title: gui.Tr.EnvTitle,
|
||||
render: gui.renderContainerEnv,
|
||||
},
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: gui.renderContainerConfig,
|
||||
},
|
||||
{
|
||||
key: "top",
|
||||
title: gui.Tr.TopTitle,
|
||||
render: gui.renderContainerTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
GetContextCacheKey: func(container *commands.Container) string {
|
||||
return "containers-" + container.ID + "-" + container.Container.State
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Container]{
|
||||
list: NewFilteredList[*commands.Container](),
|
||||
List: NewFilteredList[*commands.Container](),
|
||||
view: gui.Views.Containers,
|
||||
},
|
||||
contextIdx: 0,
|
||||
noItemsMessage: gui.Tr.NoContainers,
|
||||
NoItemsMessage: gui.Tr.NoContainers,
|
||||
gui: gui.intoInterface(),
|
||||
getContexts: func() []ContextConfig[*commands.Container] {
|
||||
return []ContextConfig[*commands.Container]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderContainerLogsToMain,
|
||||
},
|
||||
{
|
||||
key: "stats",
|
||||
title: gui.Tr.StatsTitle,
|
||||
render: gui.renderContainerStats,
|
||||
},
|
||||
{
|
||||
key: "env",
|
||||
title: gui.Tr.EnvTitle,
|
||||
render: gui.renderContainerEnv,
|
||||
},
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: gui.renderContainerConfig,
|
||||
},
|
||||
{
|
||||
key: "top",
|
||||
title: gui.Tr.TopTitle,
|
||||
render: gui.renderContainerTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
getContextCacheKey: func(container *commands.Container) string {
|
||||
return container.ID + "-" + container.Container.State
|
||||
},
|
||||
// sortedContainers returns containers sorted by state if c.SortContainersByState is true (follows 1- running, 2- exited, 3- created)
|
||||
// and sorted by name if c.SortContainersByState is false
|
||||
sort: func(a *commands.Container, b *commands.Container) bool {
|
||||
Sort: func(a *commands.Container, b *commands.Container) bool {
|
||||
return sortContainers(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
||||
},
|
||||
filter: func(container *commands.Container) bool {
|
||||
Filter: func(container *commands.Container) bool {
|
||||
// Note that this is O(N*M) time complexity where N is the number of services
|
||||
// and M is the number of containers. We expect N to be small but M may be large,
|
||||
// so we will need to keep an eye on this.
|
||||
|
|
@ -87,7 +87,7 @@ func (gui *Gui) getContainersPanel() *SideListPanel[*commands.Container] {
|
|||
|
||||
return true
|
||||
},
|
||||
getDisplayStrings: func(container *commands.Container) []string {
|
||||
GetDisplayStrings: func(container *commands.Container) []string {
|
||||
image := strings.TrimPrefix(container.Container.Image, "sha256:")
|
||||
|
||||
return []string{
|
||||
|
|
@ -261,12 +261,12 @@ func (gui *Gui) refreshContainersAndServices() error {
|
|||
}
|
||||
|
||||
// keep track of current service selected so that we can reposition our cursor if it moves position in the list
|
||||
originalSelectedLineIdx := gui.Panels.Services.selectedIdx
|
||||
selectedService, isServiceSelected := gui.Panels.Services.list.TryGet(originalSelectedLineIdx)
|
||||
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
|
||||
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
||||
|
||||
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
||||
gui.Panels.Services.list.GetAllItems(),
|
||||
gui.Panels.Containers.list.GetAllItems(),
|
||||
gui.Panels.Services.List.GetAllItems(),
|
||||
gui.Panels.Containers.List.GetAllItems(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -277,7 +277,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
|||
|
||||
// see if our selected service has moved
|
||||
if isServiceSelected {
|
||||
for i, service := range gui.Panels.Services.list.GetItems() {
|
||||
for i, service := range gui.Panels.Services.List.GetItems() {
|
||||
if service.ID == selectedService.ID {
|
||||
if i == originalSelectedLineIdx {
|
||||
break
|
||||
|
|
@ -484,7 +484,7 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
|
|||
func (gui *Gui) handleStopContainers() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||
for _, container := range gui.Panels.Containers.list.GetAllItems() {
|
||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := container.Stop(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
|
@ -498,7 +498,7 @@ func (gui *Gui) handleStopContainers() error {
|
|||
func (gui *Gui) handleRemoveContainers() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
for _, container := range gui.Panels.Containers.list.GetAllItems() {
|
||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := container.Remove(types.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ func (gui *Gui) Run() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) updateContainerDetails() error {
|
||||
return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.list.GetAllItems())
|
||||
return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.List.GetAllItems())
|
||||
}
|
||||
|
||||
func (gui *Gui) refresh() {
|
||||
|
|
@ -455,7 +455,7 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
|
|||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
for _, container := range gui.Panels.Containers.list.GetAllItems() {
|
||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if !container.MonitoringStats {
|
||||
go gui.DockerCommand.CreateClientStatMonitor(container)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,29 +18,29 @@ func (gui *Gui) getImagesPanel() *SideListPanel[*commands.Image] {
|
|||
noneLabel := "<none>"
|
||||
|
||||
return &SideListPanel[*commands.Image]{
|
||||
contextKeyPrefix: "images",
|
||||
ContextState: &ContextState[*commands.Image]{
|
||||
GetContexts: func() []ContextConfig[*commands.Image] {
|
||||
return []ContextConfig[*commands.Image]{
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: func(image *commands.Image) error {
|
||||
return gui.renderImageConfig(image)
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
GetContextCacheKey: func(image *commands.Image) string {
|
||||
return "images-" + image.ID
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Image]{
|
||||
list: NewFilteredList[*commands.Image](),
|
||||
List: NewFilteredList[*commands.Image](),
|
||||
view: gui.Views.Images,
|
||||
},
|
||||
contextIdx: 0,
|
||||
noItemsMessage: gui.Tr.NoImages,
|
||||
NoItemsMessage: gui.Tr.NoImages,
|
||||
gui: gui.intoInterface(),
|
||||
getContexts: func() []ContextConfig[*commands.Image] {
|
||||
return []ContextConfig[*commands.Image]{
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: func(image *commands.Image) error {
|
||||
return gui.renderImageConfig(image)
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
getContextCacheKey: func(image *commands.Image) string {
|
||||
return image.ID
|
||||
},
|
||||
sort: func(a *commands.Image, b *commands.Image) bool {
|
||||
Sort: func(a *commands.Image, b *commands.Image) bool {
|
||||
if a.Name == noneLabel && b.Name != noneLabel {
|
||||
return false
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ func (gui *Gui) getImagesPanel() *SideListPanel[*commands.Image] {
|
|||
|
||||
return a.ID < b.ID
|
||||
},
|
||||
getDisplayStrings: func(image *commands.Image) []string {
|
||||
GetDisplayStrings: func(image *commands.Image) []string {
|
||||
return []string{
|
||||
image.Name,
|
||||
image.Tag,
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
}
|
||||
|
||||
for _, panel := range gui.allListPanels() {
|
||||
setUpDownClickBindings(panel.View().Name(), panel.OnPrevLine, panel.OnNextLine, panel.OnClick)
|
||||
setUpDownClickBindings(panel.View().Name(), panel.HandlePrevLine, panel.HandleNextLine, panel.HandleClick)
|
||||
}
|
||||
|
||||
setUpDownClickBindings("main", gui.scrollUpMain, gui.scrollDownMain, gui.handleMainClick)
|
||||
|
|
@ -532,14 +532,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
ViewName: panel.View().Name(),
|
||||
Key: '[',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: wrappedHandler(panel.OnPrevContext),
|
||||
Handler: wrappedHandler(panel.HandlePrevContext),
|
||||
Description: gui.Tr.PreviousContext,
|
||||
},
|
||||
&Binding{
|
||||
ViewName: panel.View().Name(),
|
||||
Key: ']',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: wrappedHandler(panel.OnNextContext),
|
||||
Handler: wrappedHandler(panel.HandleNextContext),
|
||||
Description: gui.Tr.NextContext,
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,31 +12,31 @@ import (
|
|||
)
|
||||
|
||||
type ListPanel[T comparable] struct {
|
||||
selectedIdx int
|
||||
list *FilteredList[T]
|
||||
SelectedIdx int
|
||||
List *FilteredList[T]
|
||||
view *gocui.View
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) setSelectedLineIdx(value int) {
|
||||
clampedValue := 0
|
||||
if self.list.Len() > 0 {
|
||||
clampedValue = lcUtils.Clamp(value, 0, self.list.Len()-1)
|
||||
if self.List.Len() > 0 {
|
||||
clampedValue = lcUtils.Clamp(value, 0, self.List.Len()-1)
|
||||
}
|
||||
|
||||
self.selectedIdx = clampedValue
|
||||
self.SelectedIdx = clampedValue
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) clampSelectedLineIdx() {
|
||||
clamped := lcUtils.Clamp(self.selectedIdx, 0, self.list.Len()-1)
|
||||
clamped := lcUtils.Clamp(self.SelectedIdx, 0, self.List.Len()-1)
|
||||
|
||||
if clamped != self.selectedIdx {
|
||||
self.selectedIdx = clamped
|
||||
if clamped != self.SelectedIdx {
|
||||
self.SelectedIdx = clamped
|
||||
}
|
||||
}
|
||||
|
||||
// moves the cursor up or down by the given amount (up for negative values)
|
||||
func (self *ListPanel[T]) moveSelectedLine(delta int) {
|
||||
self.setSelectedLineIdx(self.selectedIdx + delta)
|
||||
self.setSelectedLineIdx(self.SelectedIdx + delta)
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) SelectNextLine() {
|
||||
|
|
@ -47,39 +47,42 @@ func (self *ListPanel[T]) SelectPrevLine() {
|
|||
self.moveSelectedLine(-1)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// list panel at the side of the screen that renders content to the main panel
|
||||
type SideListPanel[T comparable] struct {
|
||||
contextKeyPrefix string
|
||||
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
|
||||
ContextState *ContextState[T]
|
||||
|
||||
ListPanel[T]
|
||||
|
||||
// message to render in the main view if there are no items in the panel
|
||||
// and it has focus. Leave empty if you don't want to render anything
|
||||
noItemsMessage string
|
||||
NoItemsMessage string
|
||||
|
||||
gui IGui
|
||||
|
||||
// this filter is applied on top of additional default filters
|
||||
filter func(T) bool
|
||||
sort func(a, b T) bool
|
||||
// this Filter is applied on top of additional default filters
|
||||
Filter func(T) bool
|
||||
Sort func(a, b T) bool
|
||||
|
||||
onClick func(T) error
|
||||
OnClick func(T) error
|
||||
|
||||
getDisplayStrings func(T) []string
|
||||
GetDisplayStrings func(T) []string
|
||||
|
||||
// function to be called after re-rendering list. Can be nil
|
||||
onRerender func() error
|
||||
OnRerender func() error
|
||||
|
||||
// set this to true if you don't want to allow manual filtering via '/'
|
||||
disableFilter bool
|
||||
DisableFilter bool
|
||||
|
||||
// This can be nil if you want to always show the panel
|
||||
hide func() bool
|
||||
Hide func() bool
|
||||
}
|
||||
|
||||
type ISideListPanel interface {
|
||||
|
|
@ -90,11 +93,11 @@ type ISideListPanel interface {
|
|||
RerenderList() error
|
||||
IsFilterDisabled() bool
|
||||
IsHidden() bool
|
||||
OnNextLine() error
|
||||
OnPrevLine() error
|
||||
OnClick() error
|
||||
OnPrevContext() error
|
||||
OnNextContext() error
|
||||
HandleNextLine() error
|
||||
HandlePrevLine() error
|
||||
HandleClick() error
|
||||
HandlePrevContext() error
|
||||
HandleNextContext() error
|
||||
}
|
||||
|
||||
var _ ISideListPanel = &SideListPanel[int]{}
|
||||
|
|
@ -122,19 +125,19 @@ func (gui *Gui) intoInterface() IGui {
|
|||
return gui
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) OnClick() error {
|
||||
itemCount := self.list.Len()
|
||||
func (self *SideListPanel[T]) HandleClick() error {
|
||||
itemCount := self.List.Len()
|
||||
handleSelect := self.HandleSelect
|
||||
selectedLine := &self.selectedIdx
|
||||
selectedLine := &self.SelectedIdx
|
||||
|
||||
if err := self.gui.HandleClick(self.view, itemCount, selectedLine, handleSelect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.onClick != nil {
|
||||
if self.OnClick != nil {
|
||||
selectedItem, err := self.GetSelectedItem()
|
||||
if err == nil {
|
||||
return self.onClick(selectedItem)
|
||||
return self.OnClick(selectedItem)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,12 +151,12 @@ func (self *SideListPanel[T]) View() *gocui.View {
|
|||
func (self *SideListPanel[T]) HandleSelect() error {
|
||||
item, err := self.GetSelectedItem()
|
||||
if err != nil {
|
||||
if err.Error() != self.noItemsMessage {
|
||||
if err.Error() != self.NoItemsMessage {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.noItemsMessage != "" {
|
||||
return self.gui.RenderStringMain(self.noItemsMessage)
|
||||
if self.NoItemsMessage != "" {
|
||||
return self.gui.RenderStringMain(self.NoItemsMessage)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -165,97 +168,119 @@ func (self *SideListPanel[T]) HandleSelect() error {
|
|||
}
|
||||
|
||||
func (self *SideListPanel[T]) renderContext(item T) error {
|
||||
contexts := self.getContexts()
|
||||
|
||||
if len(contexts) == 0 {
|
||||
if self.ContextState == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := self.contextKeyPrefix + "-" + self.getContextCacheKey(item) + "-" + contexts[self.contextIdx].key
|
||||
key := self.ContextState.GetCurrentContextKey(item)
|
||||
if !self.gui.ShouldRefresh(key) {
|
||||
return nil
|
||||
}
|
||||
|
||||
mainView := self.gui.GetMainView()
|
||||
mainView.Tabs = self.GetContextTitles()
|
||||
mainView.TabIndex = self.contextIdx
|
||||
mainView.Tabs = self.ContextState.GetContextTitles()
|
||||
mainView.TabIndex = self.ContextState.contextIdx
|
||||
|
||||
return contexts[self.contextIdx].render(item)
|
||||
return self.ContextState.GetCurrentContext().render(item)
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) GetContextTitles() []string {
|
||||
return lo.Map(self.getContexts(), func(context ContextConfig[T], _ int) string {
|
||||
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) {
|
||||
var zero T
|
||||
|
||||
item, ok := self.list.TryGet(self.selectedIdx)
|
||||
item, ok := self.List.TryGet(self.SelectedIdx)
|
||||
if !ok {
|
||||
// could probably have a better error here
|
||||
return zero, errors.New(self.noItemsMessage)
|
||||
return zero, errors.New(self.NoItemsMessage)
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) OnNextLine() error {
|
||||
func (self *SideListPanel[T]) HandleNextLine() error {
|
||||
self.SelectNextLine()
|
||||
|
||||
return self.HandleSelect()
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) OnPrevLine() error {
|
||||
func (self *SideListPanel[T]) HandlePrevLine() error {
|
||||
self.SelectPrevLine()
|
||||
|
||||
return self.HandleSelect()
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) OnNextContext() error {
|
||||
contexts := self.getContexts()
|
||||
func (self *ContextState[T]) HandleNextContext() {
|
||||
contexts := self.GetContexts()
|
||||
|
||||
if len(contexts) == 0 {
|
||||
return nil
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.ContextState.HandleNextContext()
|
||||
|
||||
return self.HandleSelect()
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) OnPrevContext() error {
|
||||
contexts := self.getContexts()
|
||||
|
||||
if len(contexts) == 0 {
|
||||
func (self *SideListPanel[T]) HandlePrevContext() error {
|
||||
if self.ContextState == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.contextIdx = (self.contextIdx - 1 + len(contexts)) % len(contexts)
|
||||
self.ContextState.HandlePrevContext()
|
||||
|
||||
return self.HandleSelect()
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) Refocus() {
|
||||
self.gui.FocusY(self.selectedIdx, self.list.Len(), self.view)
|
||||
self.gui.FocusY(self.SelectedIdx, self.List.Len(), self.view)
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) SetItems(items []T) {
|
||||
self.list.SetItems(items)
|
||||
self.List.SetItems(items)
|
||||
self.FilterAndSort()
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) FilterAndSort() {
|
||||
filterString := self.gui.FilterString(self.view)
|
||||
|
||||
self.list.Filter(func(item T, index int) bool {
|
||||
if self.filter != nil && !self.filter(item) {
|
||||
self.List.Filter(func(item T, index int) bool {
|
||||
if self.Filter != nil && !self.Filter(item) {
|
||||
return false
|
||||
}
|
||||
|
||||
if lo.SomeBy(self.gui.IgnoreStrings(), func(ignore string) bool {
|
||||
return lo.SomeBy(self.getDisplayStrings(item), func(searchString string) bool {
|
||||
return lo.SomeBy(self.GetDisplayStrings(item), func(searchString string) bool {
|
||||
return strings.Contains(searchString, ignore)
|
||||
})
|
||||
}) {
|
||||
|
|
@ -263,7 +288,7 @@ func (self *SideListPanel[T]) FilterAndSort() {
|
|||
}
|
||||
|
||||
if filterString != "" {
|
||||
return lo.SomeBy(self.getDisplayStrings(item), func(searchString string) bool {
|
||||
return lo.SomeBy(self.GetDisplayStrings(item), func(searchString string) bool {
|
||||
return strings.Contains(searchString, filterString)
|
||||
})
|
||||
}
|
||||
|
|
@ -271,7 +296,7 @@ func (self *SideListPanel[T]) FilterAndSort() {
|
|||
return true
|
||||
})
|
||||
|
||||
self.list.Sort(self.sort)
|
||||
self.List.Sort(self.Sort)
|
||||
|
||||
self.clampSelectedLineIdx()
|
||||
}
|
||||
|
|
@ -281,8 +306,8 @@ func (self *SideListPanel[T]) RerenderList() error {
|
|||
|
||||
self.gui.Update(func() error {
|
||||
self.view.Clear()
|
||||
table := lo.Map(self.list.GetItems(), func(item T, index int) []string {
|
||||
return self.getDisplayStrings(item)
|
||||
table := lo.Map(self.List.GetItems(), func(item T, index int) []string {
|
||||
return self.GetDisplayStrings(item)
|
||||
})
|
||||
renderedTable, err := utils.RenderTable(table)
|
||||
if err != nil {
|
||||
|
|
@ -290,8 +315,8 @@ func (self *SideListPanel[T]) RerenderList() error {
|
|||
}
|
||||
fmt.Fprint(self.view, renderedTable)
|
||||
|
||||
if self.onRerender != nil {
|
||||
if err := self.onRerender(); err != nil {
|
||||
if self.OnRerender != nil {
|
||||
if err := self.OnRerender(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -306,17 +331,25 @@ func (self *SideListPanel[T]) RerenderList() error {
|
|||
}
|
||||
|
||||
func (self *SideListPanel[T]) SetContextIndex(index int) {
|
||||
if self.ContextState == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self.ContextState.SetContextIndex(index)
|
||||
}
|
||||
|
||||
func (self *ContextState[T]) SetContextIndex(index int) {
|
||||
self.contextIdx = index
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) IsFilterDisabled() bool {
|
||||
return self.disableFilter
|
||||
return self.DisableFilter
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) IsHidden() bool {
|
||||
if self.hide == nil {
|
||||
if self.Hide == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return self.hide()
|
||||
return self.Hide()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,35 +25,24 @@ type CreateMenuOptions struct {
|
|||
func (gui *Gui) getMenuPanel() *SideListPanel[*MenuItem] {
|
||||
return &SideListPanel[*MenuItem]{
|
||||
ListPanel: ListPanel[*MenuItem]{
|
||||
list: NewFilteredList[*MenuItem](),
|
||||
List: NewFilteredList[*MenuItem](),
|
||||
view: gui.Views.Menu,
|
||||
},
|
||||
noItemsMessage: "",
|
||||
NoItemsMessage: "",
|
||||
gui: gui.intoInterface(),
|
||||
onClick: gui.onMenuPress,
|
||||
sort: nil,
|
||||
getDisplayStrings: func(menuItem *MenuItem) []string {
|
||||
OnClick: gui.onMenuPress,
|
||||
Sort: nil,
|
||||
GetDisplayStrings: func(menuItem *MenuItem) []string {
|
||||
return menuItem.LabelColumns
|
||||
},
|
||||
onRerender: func() error {
|
||||
OnRerender: func() error {
|
||||
return gui.resizePopupPanel(gui.Views.Menu)
|
||||
},
|
||||
// so that we can avoid some UI trickiness, the menu will not have filtering
|
||||
// abillity yet. To support it, we would need to have filter state against
|
||||
// each panel (e.g. for when you filter the images panel, then bring up
|
||||
// the options menu, then try to filter that too.
|
||||
disableFilter: true,
|
||||
|
||||
// the menu panel doesn't actually have any contexts to display on the main view
|
||||
// so what follows are all dummy values
|
||||
contextKeyPrefix: "menu",
|
||||
contextIdx: 0,
|
||||
getContextCacheKey: func(menuItem *MenuItem) string {
|
||||
return ""
|
||||
},
|
||||
getContexts: func() []ContextConfig[*MenuItem] {
|
||||
return []ContextConfig[*MenuItem]{}
|
||||
},
|
||||
DisableFilter: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,54 +17,56 @@ import (
|
|||
|
||||
func (gui *Gui) getProjectPanel() *SideListPanel[*commands.Project] {
|
||||
return &SideListPanel[*commands.Project]{
|
||||
contextKeyPrefix: "project",
|
||||
ListPanel: ListPanel[*commands.Project]{
|
||||
list: NewFilteredList[*commands.Project](),
|
||||
view: gui.Views.Project,
|
||||
},
|
||||
contextIdx: 0,
|
||||
noItemsMessage: "",
|
||||
gui: gui.intoInterface(),
|
||||
getContexts: func() []ContextConfig[*commands.Project] {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
ContextState: &ContextState[*commands.Project]{
|
||||
GetContexts: func() []ContextConfig[*commands.Project] {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
return []ContextConfig[*commands.Project]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderAllLogs,
|
||||
},
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.DockerComposeConfigTitle,
|
||||
render: gui.renderDockerComposeConfig,
|
||||
},
|
||||
{
|
||||
key: "credits",
|
||||
title: gui.Tr.CreditsTitle,
|
||||
render: gui.renderCredits,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return []ContextConfig[*commands.Project]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderAllLogs,
|
||||
},
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.DockerComposeConfigTitle,
|
||||
render: gui.renderDockerComposeConfig,
|
||||
},
|
||||
{
|
||||
key: "credits",
|
||||
title: gui.Tr.CreditsTitle,
|
||||
render: gui.renderCredits,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
GetContextCacheKey: func(project *commands.Project) string {
|
||||
return "projects-" + project.Name
|
||||
},
|
||||
},
|
||||
|
||||
return []ContextConfig[*commands.Project]{
|
||||
{
|
||||
key: "credits",
|
||||
title: gui.Tr.CreditsTitle,
|
||||
render: gui.renderCredits,
|
||||
},
|
||||
}
|
||||
ListPanel: ListPanel[*commands.Project]{
|
||||
List: NewFilteredList[*commands.Project](),
|
||||
view: gui.Views.Project,
|
||||
},
|
||||
getContextCacheKey: func(project *commands.Project) string {
|
||||
return project.Name
|
||||
},
|
||||
sort: func(a *commands.Project, b *commands.Project) bool {
|
||||
NoItemsMessage: "",
|
||||
gui: gui.intoInterface(),
|
||||
|
||||
Sort: func(a *commands.Project, b *commands.Project) bool {
|
||||
return false
|
||||
},
|
||||
getDisplayStrings: func(project *commands.Project) []string {
|
||||
GetDisplayStrings: func(project *commands.Project) []string {
|
||||
return []string{project.Name}
|
||||
},
|
||||
// It doesn't make sense to filter a list of only one item.
|
||||
disableFilter: true,
|
||||
DisableFilter: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ func (gui *Gui) refreshProject() error {
|
|||
func (gui *Gui) getProjectName() string {
|
||||
projectName := path.Base(gui.Config.ProjectDir)
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
for _, service := range gui.Panels.Services.list.GetAllItems() {
|
||||
for _, service := range gui.Panels.Services.List.GetAllItems() {
|
||||
container := service.Container
|
||||
if container != nil && container.DetailsLoaded() {
|
||||
return container.Details.Config.Labels["com.docker.compose.project"]
|
||||
|
|
|
|||
|
|
@ -14,52 +14,52 @@ import (
|
|||
|
||||
func (gui *Gui) getServicesPanel() *SideListPanel[*commands.Service] {
|
||||
return &SideListPanel[*commands.Service]{
|
||||
contextKeyPrefix: "services",
|
||||
ContextState: &ContextState[*commands.Service]{
|
||||
GetContexts: func() []ContextConfig[*commands.Service] {
|
||||
return []ContextConfig[*commands.Service]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderServiceLogs,
|
||||
},
|
||||
{
|
||||
key: "stats",
|
||||
title: gui.Tr.StatsTitle,
|
||||
render: gui.renderServiceStats,
|
||||
},
|
||||
{
|
||||
key: "container-env",
|
||||
title: gui.Tr.ContainerEnvTitle,
|
||||
render: gui.renderServiceContainerEnv,
|
||||
},
|
||||
{
|
||||
key: "container-config",
|
||||
title: gui.Tr.ContainerConfigTitle,
|
||||
render: gui.renderServiceContainerConfig,
|
||||
},
|
||||
{
|
||||
key: "top",
|
||||
title: gui.Tr.TopTitle,
|
||||
render: gui.renderServiceTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
GetContextCacheKey: func(service *commands.Service) string {
|
||||
if service.Container == nil {
|
||||
return "services-" + service.ID
|
||||
}
|
||||
return "services-" + service.ID + "-" + service.Container.ID + "-" + service.Container.Container.State
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Service]{
|
||||
list: NewFilteredList[*commands.Service](),
|
||||
List: NewFilteredList[*commands.Service](),
|
||||
view: gui.Views.Services,
|
||||
},
|
||||
contextIdx: 0,
|
||||
// TODO: i18n
|
||||
noItemsMessage: "no service selected",
|
||||
NoItemsMessage: "no service selected",
|
||||
gui: gui.intoInterface(),
|
||||
getContexts: func() []ContextConfig[*commands.Service] {
|
||||
return []ContextConfig[*commands.Service]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderServiceLogs,
|
||||
},
|
||||
{
|
||||
key: "stats",
|
||||
title: gui.Tr.StatsTitle,
|
||||
render: gui.renderServiceStats,
|
||||
},
|
||||
{
|
||||
key: "container-env",
|
||||
title: gui.Tr.ContainerEnvTitle,
|
||||
render: gui.renderServiceContainerEnv,
|
||||
},
|
||||
{
|
||||
key: "container-config",
|
||||
title: gui.Tr.ContainerConfigTitle,
|
||||
render: gui.renderServiceContainerConfig,
|
||||
},
|
||||
{
|
||||
key: "top",
|
||||
title: gui.Tr.TopTitle,
|
||||
render: gui.renderServiceTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
getContextCacheKey: func(service *commands.Service) string {
|
||||
if service.Container == nil {
|
||||
return service.ID
|
||||
}
|
||||
return service.ID + "-" + service.Container.ID + "-" + service.Container.Container.State
|
||||
},
|
||||
// sort services first by whether they have a linked container, and second by alphabetical order
|
||||
sort: func(a *commands.Service, b *commands.Service) bool {
|
||||
Sort: func(a *commands.Service, b *commands.Service) bool {
|
||||
if a.Container != nil && b.Container == nil {
|
||||
return true
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ func (gui *Gui) getServicesPanel() *SideListPanel[*commands.Service] {
|
|||
|
||||
return a.Name < b.Name
|
||||
},
|
||||
getDisplayStrings: func(service *commands.Service) []string {
|
||||
GetDisplayStrings: func(service *commands.Service) []string {
|
||||
if service.Container == nil {
|
||||
return []string{
|
||||
utils.ColoredString("none", color.FgBlue),
|
||||
|
|
@ -90,7 +90,7 @@ func (gui *Gui) getServicesPanel() *SideListPanel[*commands.Service] {
|
|||
utils.ColoredString(cont.DisplayPorts(), color.FgYellow),
|
||||
}
|
||||
},
|
||||
hide: func() bool {
|
||||
Hide: func() bool {
|
||||
return !gui.DockerCommand.InDockerComposeProject
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,30 +13,30 @@ import (
|
|||
|
||||
func (gui *Gui) getVolumesPanel() *SideListPanel[*commands.Volume] {
|
||||
return &SideListPanel[*commands.Volume]{
|
||||
contextKeyPrefix: "volumes",
|
||||
ContextState: &ContextState[*commands.Volume]{
|
||||
GetContexts: func() []ContextConfig[*commands.Volume] {
|
||||
return []ContextConfig[*commands.Volume]{
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: gui.renderVolumeConfig,
|
||||
},
|
||||
}
|
||||
},
|
||||
GetContextCacheKey: func(volume *commands.Volume) string {
|
||||
return "volumes-" + volume.Name
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Volume]{
|
||||
list: NewFilteredList[*commands.Volume](),
|
||||
List: NewFilteredList[*commands.Volume](),
|
||||
view: gui.Views.Volumes,
|
||||
},
|
||||
contextIdx: 0,
|
||||
noItemsMessage: gui.Tr.NoVolumes,
|
||||
NoItemsMessage: gui.Tr.NoVolumes,
|
||||
gui: gui.intoInterface(),
|
||||
getContexts: func() []ContextConfig[*commands.Volume] {
|
||||
return []ContextConfig[*commands.Volume]{
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: gui.renderVolumeConfig,
|
||||
},
|
||||
}
|
||||
},
|
||||
getContextCacheKey: func(volume *commands.Volume) string {
|
||||
return volume.Name
|
||||
},
|
||||
// we're sorting these volumes based on whether they have labels defined,
|
||||
// because those are the ones you typically care about.
|
||||
// Within that, we also sort them alphabetically
|
||||
sort: func(a *commands.Volume, b *commands.Volume) bool {
|
||||
Sort: func(a *commands.Volume, b *commands.Volume) bool {
|
||||
if len(a.Volume.Labels) == 0 && len(b.Volume.Labels) > 0 {
|
||||
return false
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ func (gui *Gui) getVolumesPanel() *SideListPanel[*commands.Volume] {
|
|||
}
|
||||
return a.Name < b.Name
|
||||
},
|
||||
getDisplayStrings: func(volume *commands.Volume) []string {
|
||||
GetDisplayStrings: func(volume *commands.Volume) []string {
|
||||
return []string{volume.Volume.Driver, volume.Name}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue