From b453ce894080d82a451a3126287e28bc7624be80 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 23 Oct 2022 13:53:31 -0700 Subject: [PATCH] cleaning things up --- pkg/gui/containers_panel.go | 14 +++----- pkg/gui/filtered_list_test.go | 64 +++++++++++++++++------------------ pkg/gui/gui.go | 19 +++-------- pkg/gui/images_panel.go | 8 +++-- pkg/gui/layout.go | 2 +- pkg/gui/project_panel.go | 4 +-- pkg/gui/services_panel.go | 4 +-- pkg/gui/view_helpers.go | 52 ++++++---------------------- pkg/gui/views.go | 57 +++++++++++++++---------------- pkg/gui/volumes_panel.go | 2 +- 10 files changed, 90 insertions(+), 136 deletions(-) diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index 5f2b0947..a55e6b7c 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -121,7 +121,7 @@ func (gui *Gui) getContainersPanel() *SideListPanel[*commands.Container] { func (gui *Gui) renderContainerEnv(container *commands.Container) error { if !container.DetailsLoaded() { return gui.T.NewTask(func(stop chan struct{}) { - _ = gui.renderStringMain(gui.Tr.WaitingForContainerInfo) + _ = gui.RenderStringMain(gui.Tr.WaitingForContainerInfo) }) } @@ -130,7 +130,7 @@ func (gui *Gui) renderContainerEnv(container *commands.Container) error { mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel return gui.T.NewTask(func(stop chan struct{}) { - _ = gui.renderStringMain(gui.containerEnv(container)) + _ = gui.RenderStringMain(gui.containerEnv(container)) }) } @@ -164,7 +164,7 @@ func (gui *Gui) containerEnv(container *commands.Container) string { func (gui *Gui) renderContainerConfig(container *commands.Container) error { if !container.DetailsLoaded() { return gui.T.NewTask(func(stop chan struct{}) { - _ = gui.renderStringMain(gui.Tr.WaitingForContainerInfo) + _ = gui.RenderStringMain(gui.Tr.WaitingForContainerInfo) }) } @@ -214,7 +214,7 @@ func (gui *Gui) renderContainerConfig(container *commands.Container) error { output += fmt.Sprintf("\nFull details:\n\n%s", string(data)) return gui.T.NewTask(func(stop chan struct{}) { - _ = gui.renderStringMain(output) + _ = gui.RenderStringMain(output) }) } @@ -301,12 +301,6 @@ func (gui *Gui) renderContainersAndServices() error { return nil } -type removeContainerOption struct { - description string - command string - configOptions types.ContainerRemoveOptions -} - func (gui *Gui) handleHideStoppedContainers(g *gocui.Gui, v *gocui.View) error { gui.State.ShowExitedContainers = !gui.State.ShowExitedContainers diff --git a/pkg/gui/filtered_list_test.go b/pkg/gui/filtered_list_test.go index 2ff7d37e..00d4c5ac 100644 --- a/pkg/gui/filtered_list_test.go +++ b/pkg/gui/filtered_list_test.go @@ -8,22 +8,22 @@ import ( func TestFilteredListGet(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] args int want int }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: 1, want: 2, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: 2, want: 3, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, args: 0, want: 2, }, @@ -38,15 +38,15 @@ func TestFilteredListGet(t *testing.T) { func TestFilteredListLen(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] want int }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, want: 3, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, want: 1, }, } @@ -60,19 +60,19 @@ func TestFilteredListLen(t *testing.T) { func TestFilteredListFilter(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] args func(int, int) bool - want FilteredList[int] + want *FilteredList[int] }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: func(i int, _ int) bool { return i%2 == 0 }, - want: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, + want: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: func(i int, _ int) bool { return i%2 == 1 }, - want: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 2}}, + want: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 2}}, }, } @@ -84,19 +84,19 @@ func TestFilteredListFilter(t *testing.T) { func TestFilteredListSort(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] args func(int, int) bool - want FilteredList[int] + want *FilteredList[int] }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: func(i int, j int) bool { return i < j }, - want: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + want: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: func(i int, j int) bool { return i > j }, - want: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{2, 1, 0}}, + want: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{2, 1, 0}}, }, } @@ -108,22 +108,22 @@ func TestFilteredListSort(t *testing.T) { func TestFilteredListGetIndex(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] args int want int }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: 1, want: 0, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: 2, want: 1, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, args: 0, want: -1, }, @@ -138,15 +138,15 @@ func TestFilteredListGetIndex(t *testing.T) { func TestFilteredListGetItems(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] want []int }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, want: []int{1, 2, 3}, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, want: []int{2}, }, } @@ -159,19 +159,19 @@ func TestFilteredListGetItems(t *testing.T) { func TestFilteredListSetItems(t *testing.T) { tests := []struct { - f FilteredList[int] + f *FilteredList[int] args []int - want FilteredList[int] + want *FilteredList[int] }{ { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{0, 1, 2}}, args: []int{4, 5, 6}, - want: FilteredList[int]{allItems: []int{4, 5, 6}, indices: []int{0, 1, 2}}, + want: &FilteredList[int]{allItems: []int{4, 5, 6}, indices: []int{0, 1, 2}}, }, { - f: FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, + f: &FilteredList[int]{allItems: []int{1, 2, 3}, indices: []int{1}}, args: []int{4}, - want: FilteredList[int]{allItems: []int{4}, indices: []int{0}}, + want: &FilteredList[int]{allItems: []int{4}, indices: []int{0}}, }, } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 1d280e46..6b3e9478 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -79,7 +79,7 @@ type guiState struct { SubProcessOutput string Stats map[string]commands.ContainerStats - // if true, we show containers with an 'exited' status in the contaniners panel + // if true, we show containers with an 'exited' status in the containers panel ShowExitedContainers bool ScreenMode WindowMaximisation @@ -138,7 +138,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand } deadlock.Opts.Disable = !gui.Config.Debug - deadlock.Opts.DeadlockTimeout = 5 * time.Second + deadlock.Opts.DeadlockTimeout = 10 * time.Second return gui, nil } @@ -257,7 +257,8 @@ func (gui *Gui) Run() error { gui.goEvery(time.Millisecond*30, gui.reRenderMain) gui.goEvery(time.Millisecond*1000, gui.updateContainerDetails) gui.goEvery(time.Millisecond*1000, gui.checkForContextChange) - gui.goEvery(time.Millisecond*1000, gui.rerenderContainersAndServices) + // we need to regularly re-render these because their stats will be changed in the background + gui.goEvery(time.Millisecond*1000, gui.renderContainersAndServices) }() err = g.MainLoop() @@ -271,12 +272,6 @@ func (gui *Gui) updateContainerDetails() error { return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.list.GetAllItems()) } -func (gui *Gui) rerenderContainersAndServices() error { - // we need to regularly re-render these because their stats will be changed in the background - gui.renderContainersAndServices() - return nil -} - func (gui *Gui) refresh() { go func() { if err := gui.refreshProject(); err != nil { @@ -426,7 +421,7 @@ func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error { }) } -func (gui *Gui) shouldRefresh(key string) bool { +func (gui *Gui) ShouldRefresh(key string) bool { if gui.State.Panels.Main.ObjectKey == key { return false } @@ -435,10 +430,6 @@ func (gui *Gui) shouldRefresh(key string) bool { return true } -func (gui *Gui) ShouldRefresh(key string) bool { - return gui.shouldRefresh(key) -} - func (gui *Gui) initiallyFocusedViewName() string { if gui.DockerCommand.InDockerComposeProject { return "services" diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go index 80a353f1..11759028 100644 --- a/pkg/gui/images_panel.go +++ b/pkg/gui/images_panel.go @@ -60,7 +60,11 @@ func (gui *Gui) getImagesPanel() *SideListPanel[*commands.Image] { return a.ID < b.ID }, getDisplayStrings: func(image *commands.Image) []string { - return []string{image.Name, image.Tag, utils.FormatDecimalBytes(int(image.Image.Size))} + return []string{ + image.Name, + image.Tag, + utils.FormatDecimalBytes(int(image.Image.Size)), + } }, } } @@ -86,7 +90,7 @@ func (gui *Gui) renderImageConfig(image *commands.Image) error { mainView.Autoscroll = false mainView.Wrap = false // don't care what your config is this page is ugly without wrapping - _ = gui.renderStringMain(output) + _ = gui.RenderStringMain(output) }) } diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go index 0933ffb5..ea28f507 100644 --- a/pkg/gui/layout.go +++ b/pkg/gui/layout.go @@ -101,7 +101,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { return view, err } - for _, viewName := range gui.controlledBoundsViewNames() { + for _, viewName := range gui.autoPositionedViewNames() { _, err := setViewFromDimensions(viewName, viewName) if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG { return err diff --git a/pkg/gui/project_panel.go b/pkg/gui/project_panel.go index 083f5edc..17b5c9a9 100644 --- a/pkg/gui/project_panel.go +++ b/pkg/gui/project_panel.go @@ -108,7 +108,7 @@ func (gui *Gui) renderCredits(_project *commands.Project) error { configBuf.String(), }, "\n\n") - _ = gui.renderStringMain(dashboardString) + _ = gui.RenderStringMain(dashboardString) }) } @@ -151,7 +151,7 @@ func (gui *Gui) renderDockerComposeConfig(_project *commands.Project) error { mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel config := gui.DockerCommand.DockerComposeConfig() - _ = gui.renderStringMain(config) + _ = gui.RenderStringMain(config) }) } diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go index 6fc71e9d..47d099e3 100644 --- a/pkg/gui/services_panel.go +++ b/pkg/gui/services_panel.go @@ -98,7 +98,7 @@ func (gui *Gui) getServicesPanel() *SideListPanel[*commands.Service] { func (gui *Gui) renderServiceContainerConfig(service *commands.Service) error { if service.Container == nil { - return gui.renderStringMain(gui.Tr.NoContainer) + return gui.RenderStringMain(gui.Tr.NoContainer) } return gui.renderContainerConfig(service.Container) @@ -106,7 +106,7 @@ func (gui *Gui) renderServiceContainerConfig(service *commands.Service) error { func (gui *Gui) renderServiceContainerEnv(service *commands.Service) error { if service.Container == nil { - return gui.renderStringMain(gui.Tr.NoContainer) + return gui.RenderStringMain(gui.Tr.NoContainer) } return gui.renderContainerEnv(service.Container) diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 6a7396e9..70cb4996 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -106,13 +106,8 @@ func (gui *Gui) focusPoint(selectedX int, selectedY int, lineCount int, v *gocui } } -func (gui *Gui) focusY(selectedY int, lineCount int, v *gocui.View) { - gui.focusPoint(0, selectedY, lineCount, v) -} - -// TODO: combine with above func (gui *Gui) FocusY(selectedY int, lineCount int, v *gocui.View) { - gui.focusY(selectedY, lineCount, v) + gui.focusPoint(0, selectedY, lineCount, v) } func (gui *Gui) ResetOrigin(v *gocui.View) { @@ -149,13 +144,8 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error { return nil } -func (gui *Gui) renderStringMain(s string) error { - return gui.renderString(gui.g, "main", s) -} - -// TODO: merge with above func (gui *Gui) RenderStringMain(s string) error { - return gui.renderStringMain(s) + return gui.renderString(gui.g, "main", s) } // reRenderString sets the main view's content, without changing its origin @@ -225,22 +215,6 @@ func (gui *Gui) resizePopupPanel(v *gocui.View) error { return err } -func (gui *Gui) changeSelectedLine(line *int, total int, up bool) { - if up { - if *line == -1 || *line == 0 { - return - } - - *line -= 1 - } else { - if *line == -1 || *line == total-1 { - return - } - - *line += 1 - } -} - func (gui *Gui) renderPanelOptions() error { currentView := gui.g.CurrentView() switch currentView.Name() { @@ -260,11 +234,6 @@ func (gui *Gui) popupPanelFocused() bool { return gui.isPopupPanel(gui.currentViewName()) } -// TODO: merge into above -func (gui *Gui) PopupPanelFocused() bool { - return gui.popupPanelFocused() -} - func (gui *Gui) clearMainView() { mainView := gui.Views.Main mainView.Clear() @@ -272,7 +241,14 @@ func (gui *Gui) clearMainView() { _ = mainView.SetCursor(0, 0) } -func (gui *Gui) handleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func(*gocui.Gui, *gocui.View) error) error { +func (gui *Gui) HandleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func() error) error { + wrappedHandleSelect := func(g *gocui.Gui, v *gocui.View) error { + return handleSelect() + } + return gui.handleClickAux(v, itemCount, selectedLine, wrappedHandleSelect) +} + +func (gui *Gui) handleClickAux(v *gocui.View, itemCount int, selectedLine *int, handleSelect func(*gocui.Gui, *gocui.View) error) error { if gui.popupPanelFocused() && v != nil && !gui.isPopupPanel(v.Name()) { return nil } @@ -301,14 +277,6 @@ func (gui *Gui) handleClick(v *gocui.View, itemCount int, selectedLine *int, han return handleSelect(gui.g, v) } -// TODO: combine with above -func (gui *Gui) HandleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func() error) error { - wrappedHandleSelect := func(g *gocui.Gui, v *gocui.View) error { - return handleSelect() - } - return gui.handleClick(v, itemCount, selectedLine, wrappedHandleSelect) -} - func (gui *Gui) nextScreenMode() error { if gui.currentViewName() == "main" { gui.State.ScreenMode = prevIntInCycle([]WindowMaximisation{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode) diff --git a/pkg/gui/views.go b/pkg/gui/views.go index 4f75ba62..2d63faf7 100644 --- a/pkg/gui/views.go +++ b/pkg/gui/views.go @@ -3,6 +3,7 @@ package gui import ( "github.com/fatih/color" "github.com/jesseduffield/gocui" + "github.com/samber/lo" ) type Views struct { @@ -20,7 +21,7 @@ type Views struct { Options *gocui.View Information *gocui.View AppStatus *gocui.View - // text that prompts you to enter text in the Search view + // text that prompts you to enter text in the Filter view FilterPrefix *gocui.View // appears next to the SearchPrefix view, it's where you type in the search string Filter *gocui.View @@ -36,33 +37,36 @@ type Views struct { type viewNameMapping struct { viewPtr **gocui.View name string + // if true, we handle the position/size of the view in arrangement.go. Otherwise + // we handle it manually. + autoPosition bool } func (gui *Gui) orderedViewNameMappings() []viewNameMapping { return []viewNameMapping{ // first layer. Ordering within this layer does not matter because there are // no overlapping views - {viewPtr: &gui.Views.Project, name: "project"}, - {viewPtr: &gui.Views.Services, name: "services"}, - {viewPtr: &gui.Views.Containers, name: "containers"}, - {viewPtr: &gui.Views.Images, name: "images"}, - {viewPtr: &gui.Views.Volumes, name: "volumes"}, + {viewPtr: &gui.Views.Project, name: "project", autoPosition: true}, + {viewPtr: &gui.Views.Services, name: "services", autoPosition: true}, + {viewPtr: &gui.Views.Containers, name: "containers", autoPosition: true}, + {viewPtr: &gui.Views.Images, name: "images", autoPosition: true}, + {viewPtr: &gui.Views.Volumes, name: "volumes", autoPosition: true}, - {viewPtr: &gui.Views.Main, name: "main"}, + {viewPtr: &gui.Views.Main, name: "main", autoPosition: true}, // bottom line - {viewPtr: &gui.Views.Options, name: "options"}, - {viewPtr: &gui.Views.AppStatus, name: "appStatus"}, - {viewPtr: &gui.Views.Information, name: "information"}, - {viewPtr: &gui.Views.Filter, name: "filter"}, - {viewPtr: &gui.Views.FilterPrefix, name: "filterPrefix"}, + {viewPtr: &gui.Views.Options, name: "options", autoPosition: true}, + {viewPtr: &gui.Views.AppStatus, name: "appStatus", autoPosition: true}, + {viewPtr: &gui.Views.Information, name: "information", autoPosition: true}, + {viewPtr: &gui.Views.Filter, name: "filter", autoPosition: true}, + {viewPtr: &gui.Views.FilterPrefix, name: "filterPrefix", autoPosition: true}, // popups. - {viewPtr: &gui.Views.Menu, name: "menu"}, - {viewPtr: &gui.Views.Confirmation, name: "confirmation"}, + {viewPtr: &gui.Views.Menu, name: "menu", autoPosition: false}, + {viewPtr: &gui.Views.Confirmation, name: "confirmation", autoPosition: false}, // this guy will cover everything else when it appears - {viewPtr: &gui.Views.Limit, name: "limit"}, + {viewPtr: &gui.Views.Limit, name: "limit", autoPosition: true}, } } @@ -155,19 +159,12 @@ func (gui *Gui) popupViewNames() []string { } // these views have their position and size determined by arrangement.go -func (gui *Gui) controlledBoundsViewNames() []string { - return []string{ - "project", - "services", - "containers", - "images", - "volumes", - "options", - "information", - "appStatus", - "main", - "limit", - "filterPrefix", - "filter", - } +func (gui *Gui) autoPositionedViewNames() []string { + views := lo.Filter(gui.orderedViewNameMappings(), func(viewNameMapping viewNameMapping, _ int) bool { + return viewNameMapping.autoPosition + }) + + return lo.Map(views, func(viewNameMapping viewNameMapping, _ int) string { + return viewNameMapping.name + }) } diff --git a/pkg/gui/volumes_panel.go b/pkg/gui/volumes_panel.go index 3c63004e..4ce711e2 100644 --- a/pkg/gui/volumes_panel.go +++ b/pkg/gui/volumes_panel.go @@ -81,7 +81,7 @@ func (gui *Gui) renderVolumeConfig(volume *commands.Volume) error { output += utils.WithPadding("Size: ", padding) + utils.FormatBinaryBytes(int(volume.Volume.UsageData.Size)) + "\n" } - _ = gui.renderStringMain(output) + _ = gui.RenderStringMain(output) }) }