From 540c592b0459dcc263d00f9b2fef1167afe3eead Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 9 Oct 2022 17:48:42 -0700 Subject: [PATCH] prevent issue where opening popup in maximised main view shows popup in front of a side view --- pkg/gui/arrangement.go | 2 +- pkg/gui/confirmation_panel.go | 18 +++++++++--------- pkg/gui/containers_panel.go | 10 +++++----- pkg/gui/gui.go | 2 +- pkg/gui/images_panel.go | 4 ++-- pkg/gui/main_panel.go | 2 +- pkg/gui/menu_panel.go | 12 +++++++++--- pkg/gui/options_menu_panel.go | 5 +---- pkg/gui/services_panel.go | 2 +- pkg/gui/view_helpers.go | 19 +++++++++++++++++-- pkg/gui/views.go | 4 ++++ pkg/gui/volumes_panel.go | 2 +- pkg/gui/window.go | 5 +++++ 13 files changed, 57 insertions(+), 30 deletions(-) diff --git a/pkg/gui/arrangement.go b/pkg/gui/arrangement.go index 6b0250e5..df717341 100644 --- a/pkg/gui/arrangement.go +++ b/pkg/gui/arrangement.go @@ -75,7 +75,7 @@ func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V { } func (gui *Gui) getMidSectionWeights() (int, int) { - currentWindow := gui.currentWindow() + currentWindow := gui.currentStaticWindowName() // we originally specified this as a ratio i.e. .20 would correspond to a weight of 1 against 4 sidePanelWidthRatio := gui.Config.UserConfig.Gui.SidePanelWidth diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index 85f3fe85..c515fd54 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -30,7 +30,7 @@ func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.Vie } func (gui *Gui) closeConfirmationPrompt() error { - if err := gui.returnFocus(gui.g, gui.Views.Confirmation); err != nil { + if err := gui.returnFocus(); err != nil { return err } gui.g.DeleteViewKeybindings("confirmation") @@ -64,7 +64,7 @@ func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, wrap bool, prompt s func (gui *Gui) createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error { gui.onNewPopupPanel() - err := gui.prepareConfirmationPanel(currentView, title, "", false) + err := gui.prepareConfirmationPanel(title, "", false) if err != nil { return err } @@ -72,7 +72,7 @@ func (gui *Gui) createPromptPanel(g *gocui.Gui, currentView *gocui.View, title s return gui.setKeyBindings(g, handleConfirm, nil) } -func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt string, hasLoader bool) error { +func (gui *Gui) prepareConfirmationPanel(title, prompt string, hasLoader bool) error { x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, true, prompt) confirmationView := gui.Views.Confirmation _, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0) @@ -99,19 +99,19 @@ func (gui *Gui) onNewPopupPanel() { // It is very important that within this function we never include the original prompt in any error messages, because it may contain e.g. a user password. // The golangcilint unparam linter complains that handleClose is alwans nil but one day it won't be nil. // nolint:unparam -func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { - return gui.createPopupPanel(g, currentView, title, prompt, false, handleConfirm, handleClose) +func (gui *Gui) createConfirmationPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { + return gui.createPopupPanel(title, prompt, false, handleConfirm, handleClose) } -func (gui *Gui) createPopupPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, hasLoader bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { +func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { gui.onNewPopupPanel() - g.Update(func(g *gocui.Gui) error { + gui.g.Update(func(g *gocui.Gui) error { if gui.currentViewName() == "confirmation" { if err := gui.closeConfirmationPrompt(); err != nil { gui.Log.Error(err.Error()) } } - err := gui.prepareConfirmationPanel(currentView, title, prompt, hasLoader) + err := gui.prepareConfirmationPanel(title, prompt, hasLoader) if err != nil { return err } @@ -146,7 +146,7 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go func (gui *Gui) createErrorPanel(g *gocui.Gui, message string) error { colorFunction := color.New(color.FgRed).SprintFunc() coloredMessage := colorFunction(strings.TrimSpace(message)) - return gui.createConfirmationPanel(gui.g, g.CurrentView(), gui.Tr.ErrorTitle, coloredMessage, nil, nil) + return gui.createConfirmationPanel(gui.Tr.ErrorTitle, coloredMessage, nil, nil) } func (gui *Gui) renderConfirmationOptions() error { diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index e4309e72..3dc9bd2f 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -408,7 +408,7 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error { if err := container.Remove(configOptions); err != nil { if commands.HasErrorCode(err, commands.MustStopContainer) { - return gui.createConfirmationPanel(gui.g, v, gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error { configOptions.Force = true return container.Remove(configOptions) @@ -455,7 +455,7 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(gui.g, v, gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error { if err := container.Stop(); err != nil { return gui.createErrorPanel(gui.g, err.Error()) @@ -496,7 +496,7 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handlePruneContainers() error { - return gui.createConfirmationPanel(gui.g, gui.getContainersView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneContainers, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneContainers, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error { err := gui.DockerCommand.PruneContainers() if err != nil { @@ -555,7 +555,7 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error } func (gui *Gui) handleStopContainers() error { - return gui.createConfirmationPanel(gui.g, gui.getContainersView(), gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) 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.DockerCommand.Containers { _ = container.Stop() @@ -567,7 +567,7 @@ func (gui *Gui) handleStopContainers() error { } func (gui *Gui) handleRemoveContainers() error { - return gui.createConfirmationPanel(gui.g, gui.getContainersView(), gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) 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.DockerCommand.Containers { _ = container.Remove(types.ContainerRemoveOptions{Force: true}) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 1b2199a7..94aa4c0d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -385,7 +385,7 @@ func (gui *Gui) reRenderMain() error { func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error { if gui.Config.UserConfig.ConfirmOnQuit { - return gui.createConfirmationPanel(g, v, "", gui.Tr.ConfirmQuit, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel("", gui.Tr.ConfirmQuit, func(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit }, nil) } diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go index da69c93f..2f513ab5 100644 --- a/pkg/gui/images_panel.go +++ b/pkg/gui/images_panel.go @@ -116,7 +116,7 @@ func (gui *Gui) refreshImages() error { gui.g.Update(func(g *gocui.Gui) error { ImagesView.Clear() - isFocused := gui.g.CurrentView().Name() == "Images" + isFocused := gui.g.CurrentView() == gui.Views.Images list, err := utils.RenderList(gui.DockerCommand.Images, utils.IsFocused(isFocused)) if err != nil { return err @@ -260,7 +260,7 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handlePruneImages() error { - return gui.createConfirmationPanel(gui.g, gui.getImagesView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneImages, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneImages, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error { err := gui.DockerCommand.PruneImages() if err != nil { diff --git a/pkg/gui/main_panel.go b/pkg/gui/main_panel.go index c8f13721..e8675df4 100644 --- a/pkg/gui/main_panel.go +++ b/pkg/gui/main_panel.go @@ -106,7 +106,7 @@ func (gui *Gui) handleEnterMain(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleExitMain(g *gocui.Gui, v *gocui.View) error { v.ParentView = nil - return gui.returnFocus(gui.g, v) + return gui.returnFocus() } func (gui *Gui) handleMainClick(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index f6e5e74b..66e0d316 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -58,7 +58,7 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { } } gui.Views.Menu.Visible = false - return gui.returnFocus(g, v) + return gui.returnFocus() } func (gui *Gui) createMenu(title string, items interface{}, itemCount int, handlePress func(int) error) error { @@ -80,12 +80,18 @@ func (gui *Gui) createMenu(title string, items interface{}, itemCount int, handl wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error { selectedLine := gui.State.Panels.Menu.SelectedLine + + menuView.Visible = false + err := gui.returnFocus() + if err != nil { + return err + } + if err := handlePress(selectedLine); err != nil { return err } - menuView.Visible = false - return gui.returnFocus(gui.g, menuView) + return nil } gui.State.Panels.Menu.OnPress = wrappedHandlePress diff --git a/pkg/gui/options_menu_panel.go b/pkg/gui/options_menu_panel.go index 24dd9681..c5a4c2b6 100644 --- a/pkg/gui/options_menu_panel.go +++ b/pkg/gui/options_menu_panel.go @@ -64,10 +64,7 @@ func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error { if index >= len(bindings) { return errors.New("Index is greater than size of bindings") } - err := gui.handleMenuClose(g, v) - if err != nil { - return err - } + return bindings[index].Handler(g, v) } diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go index 09a4dafb..16accf28 100644 --- a/pkg/gui/services_panel.go +++ b/pkg/gui/services_panel.go @@ -237,7 +237,7 @@ func (gui *Gui) handleServiceStop(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(gui.g, v, gui.Tr.Confirm, gui.Tr.StopService, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopService, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error { if err := service.Stop(); err != nil { return gui.createErrorPanel(gui.g, err.Error()) diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 2ab39f19..740ccbc1 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -119,7 +119,7 @@ func (gui *Gui) switchFocusAux(newView *gocui.View) error { return gui.newLineFocused(newView) } -func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) returnFocus() error { gui.Mutexes.ViewStackMutex.Lock() defer gui.Mutexes.ViewStackMutex.Unlock() @@ -128,7 +128,7 @@ func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error { } previousViewName := gui.State.ViewStack[len(gui.State.ViewStack)-2] - previousView, err := g.View(previousViewName) + previousView, err := gui.g.View(previousViewName) if err != nil { return err } @@ -153,6 +153,21 @@ func (gui *Gui) pushView(name string) { }) gui.State.ViewStack = append(gui.State.ViewStack, name) + gui.Log.Warnf("Just pushed %s. View stack is now "+strings.Join(gui.State.ViewStack, ", "), name) +} + +// excludes popups +func (gui *Gui) currentStaticViewName() string { + gui.Mutexes.ViewStackMutex.Lock() + defer gui.Mutexes.ViewStackMutex.Unlock() + + for i := len(gui.State.ViewStack) - 1; i >= 0; i-- { + if !lo.Contains(gui.popupViewNames(), gui.State.ViewStack[i]) { + return gui.State.ViewStack[i] + } + } + + return gui.initiallyFocusedViewName() } // if the cursor down past the last item, move it to the last line diff --git a/pkg/gui/views.go b/pkg/gui/views.go index 3de54c26..6a7ed3e6 100644 --- a/pkg/gui/views.go +++ b/pkg/gui/views.go @@ -127,3 +127,7 @@ func (gui *Gui) getInformationContent() string { donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.Donate) return donate + " " + informationStr } + +func (gui *Gui) popupViewNames() []string { + return []string{"confirmation", "menu"} +} diff --git a/pkg/gui/volumes_panel.go b/pkg/gui/volumes_panel.go index 6d69e95a..d052856d 100644 --- a/pkg/gui/volumes_panel.go +++ b/pkg/gui/volumes_panel.go @@ -239,7 +239,7 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handlePruneVolumes() error { - return gui.createConfirmationPanel(gui.g, gui.getVolumesView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error { err := gui.DockerCommand.PruneVolumes() if err != nil { diff --git a/pkg/gui/window.go b/pkg/gui/window.go index 42c12dd5..f596a2b2 100644 --- a/pkg/gui/window.go +++ b/pkg/gui/window.go @@ -5,3 +5,8 @@ func (gui *Gui) currentWindow() string { // are using the view name as the window name return gui.currentViewName() } + +// excludes popups +func (gui *Gui) currentStaticWindowName() string { + return gui.currentStaticViewName() +}