move context stuff into its own struct

pull/392/head
Jesse Duffield 2 years ago
parent c6d424e4a4
commit 5d498c796a

@ -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)
}
// list panel at the side of the screen that renders content to the main panel
type SideListPanel[T comparable] struct {
contextKeyPrefix string
contextIdx int
type ContextState[T any] struct {
contextIdx int
// contexts []ContextConfig[T]
getContexts func() []ContextConfig[T]
GetContexts func() []ContextConfig[T]
// this tells us whether we need to re-render to the main panel
getContextCacheKey func(item T) string
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 {
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)
return self.HandleSelect()
}
func (self *SideListPanel[T]) OnPrevContext() error {
contexts := self.getContexts()
func (self *ContextState[T]) HandlePrevContext() {
contexts := self.GetContexts()
if len(contexts) == 0 {
return nil
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]) HandlePrevContext() error {
if self.ContextState == nil {
return nil
}
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,
},
}
}
return []ContextConfig[*commands.Project]{
{
key: "credits",
title: gui.Tr.CreditsTitle,
render: gui.renderCredits,
},
}
},
GetContextCacheKey: func(project *commands.Project) string {
return "projects-" + project.Name
},
},
getContextCacheKey: func(project *commands.Project) string {
return project.Name
ListPanel: ListPanel[*commands.Project]{
List: NewFilteredList[*commands.Project](),
view: gui.Views.Project,
},
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…
Cancel
Save