support custom commands and do minor refactor

This commit is contained in:
Jesse Duffield 2019-06-05 19:58:08 +10:00
parent 645f588cb1
commit c55e83aeaa
8 changed files with 340 additions and 318 deletions

View file

@ -158,15 +158,6 @@ func (gui *Gui) renderLogsForRegularContainer(mainView *gocui.View, container *c
return nil return nil
} }
func (gui *Gui) obtainLock() {
gui.State.MainProcessChan <- struct{}{}
gui.State.MainProcessMutex.Lock()
}
func (gui *Gui) releaseLock() {
gui.State.MainProcessMutex.Unlock()
}
func (gui *Gui) runProcessWithLock(cmd *exec.Cmd) { func (gui *Gui) runProcessWithLock(cmd *exec.Cmd) {
gui.T.NewTask(func(stop chan struct{}) { gui.T.NewTask(func(stop chan struct{}) {
cmd.Start() cmd.Start()

View file

@ -1,7 +1,6 @@
package gui package gui
import ( import (
"math"
"sync" "sync"
// "io" // "io"
@ -19,7 +18,6 @@ import (
"github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/i18n" "github.com/jesseduffield/lazydocker/pkg/i18n"
"github.com/jesseduffield/lazydocker/pkg/tasks" "github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -109,8 +107,6 @@ type guiState struct {
Platform commands.Platform Platform commands.Platform
Panels *panelStates Panels *panelStates
SubProcessOutput string SubProcessOutput string
MainProcessMutex sync.Mutex
MainProcessChan chan struct{}
Stats map[string]commands.ContainerStats Stats map[string]commands.ContainerStats
} }
@ -129,17 +125,8 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
ObjectKey: "", ObjectKey: "",
}, },
}, },
MainProcessChan: make(chan struct{}),
} }
go func() {
// setting up a goroutine for listening to the first stop signal on this channel
// because whenever something wants to lock the mutex, it tells the existing process to stop
// but on startup we don't have a process so we just mock it
// this is because we're using an unbuffered channel
<-initialState.MainProcessChan
}()
gui := &Gui{ gui := &Gui{
Log: log, Log: log,
DockerCommand: dockerCommand, DockerCommand: dockerCommand,
@ -158,32 +145,6 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
return gui, nil return gui, nil
} }
func (gui *Gui) scrollUpMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main")
mainView.Autoscroll = false
ox, oy := mainView.Origin()
newOy := int(math.Max(0, float64(oy-gui.Config.UserConfig.Gui.ScrollHeight)))
return mainView.SetOrigin(ox, newOy)
}
func (gui *Gui) scrollDownMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main")
ox, oy := mainView.Origin()
y := oy
if !gui.Config.UserConfig.Gui.ScrollPastBottom {
_, sy := mainView.Size()
y += sy
}
// for some reason we can't work out whether we've hit the bottomq
// there is a large discrepancy in the origin's y value and the length of BufferLines
return mainView.SetOrigin(ox, oy+gui.Config.UserConfig.Gui.ScrollHeight)
}
func (gui *Gui) autoScrollMain(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Autoscroll = true
return nil
}
func (gui *Gui) handleRefresh(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleRefresh(g *gocui.Gui, v *gocui.View) error {
return gui.refreshSidePanels(g) return gui.refreshSidePanels(g)
} }
@ -195,260 +156,6 @@ func max(a, b int) int {
return b return b
} }
// getFocusLayout returns a manager function for when view gain and lose focus
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
var previousView *gocui.View
return func(g *gocui.Gui) error {
newView := gui.g.CurrentView()
if err := gui.onFocusChange(); err != nil {
return err
}
// for now we don't consider losing focus to a popup panel as actually losing focus
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
if err := gui.onFocusLost(previousView, newView); err != nil {
return err
}
if err := gui.onFocus(newView); err != nil {
return err
}
previousView = newView
}
return nil
}
}
func (gui *Gui) onFocusChange() error {
currentView := gui.g.CurrentView()
for _, view := range gui.g.Views() {
view.Highlight = view == currentView
}
return nil
}
func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
if v == nil {
return nil
}
gui.Log.Info(v.Name() + " focus lost")
return nil
}
func (gui *Gui) onFocus(v *gocui.View) error {
if v == nil {
return nil
}
gui.Log.Info(v.Name() + " focus gained")
return nil
}
// layout is called for every screen re-render e.g. when the screen is resized
func (gui *Gui) layout(g *gocui.Gui) error {
g.Highlight = true
width, height := g.Size()
information := gui.Config.Version
minimumHeight := 9
minimumWidth := 10
if height < minimumHeight || width < minimumWidth {
v, err := g.SetView("limit", 0, 0, width-1, height-1, 0)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
v.Wrap = true
_, _ = g.SetViewOnTop("limit")
}
return nil
}
currView := gui.g.CurrentView()
currentCyclebleView := gui.State.PreviousView
if currView != nil {
viewName := currView.Name()
usePreviouseView := true
for _, view := range cyclableViews {
if view == viewName {
currentCyclebleView = viewName
usePreviouseView = false
break
}
}
if usePreviouseView {
currentCyclebleView = gui.State.PreviousView
}
}
usableSpace := height - 4
tallPanels := 3
vHeights := map[string]int{
"status": tallPanels,
"services": usableSpace/tallPanels + usableSpace%tallPanels,
"containers": usableSpace / tallPanels,
"images": usableSpace / tallPanels,
"options": 1,
}
if height < 28 {
defaultHeight := 3
if height < 21 {
defaultHeight = 1
}
vHeights = map[string]int{
"status": defaultHeight,
"services": defaultHeight,
"containers": defaultHeight,
"images": defaultHeight,
"options": defaultHeight,
}
vHeights[currentCyclebleView] = height - defaultHeight*tallPanels - 1
}
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)
leftSideWidth := width / 3
appStatus := gui.statusManager.getStatusString()
appStatusOptionsBoundary := 0
if appStatus != "" {
appStatusOptionsBoundary = len(appStatus) + 2
}
_, _ = g.SetViewOnBottom("limit")
g.DeleteView("limit")
v, err := g.SetView("main", leftSideWidth+1, 0, width-1, height-2, gocui.LEFT)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Wrap = true
v.FgColor = gocui.ColorWhite
}
if v, err := g.SetView("status", 0, 0, leftSideWidth, vHeights["status"]-1, gocui.BOTTOM|gocui.RIGHT); err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("StatusTitle")
v.FgColor = gocui.ColorWhite
}
servicesView, err := g.SetViewBeneath("services", "status", vHeights["services"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
servicesView.Highlight = true
servicesView.Title = gui.Tr.SLocalize("ServicesTitle")
servicesView.FgColor = gocui.ColorWhite
gui.focusPoint(0, gui.State.Panels.Services.SelectedLine, len(gui.DockerCommand.Services), servicesView)
}
containersView, err := g.SetViewBeneath("containers", "services", vHeights["containers"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
containersView.Highlight = true
containersView.Title = gui.Tr.SLocalize("ContainersTitle")
containersView.FgColor = gocui.ColorWhite
gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.Containers), containersView)
}
imagesView, err := g.SetViewBeneath("images", "containers", vHeights["images"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
imagesView.Highlight = true
imagesView.Title = gui.Tr.SLocalize("ImagesTitle")
imagesView.FgColor = gocui.ColorWhite
gui.focusPoint(0, gui.State.Panels.Images.SelectedLine, len(gui.DockerCommand.Images), imagesView)
}
if v, err := g.SetView("options", appStatusOptionsBoundary-1, height-2, optionsVersionBoundary-1, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
v.Frame = false
if v.FgColor, err = gui.GetOptionsPanelTextColor(); err != nil {
return err
}
}
if appStatusView, err := g.SetView("appStatus", -1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
appStatusView.BgColor = gocui.ColorDefault
appStatusView.FgColor = gocui.ColorCyan
appStatusView.Frame = false
if _, err := g.SetViewOnBottom("appStatus"); err != nil {
return err
}
}
if v, err := g.SetView("information", optionsVersionBoundary-1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
v.BgColor = gocui.ColorDefault
v.FgColor = gocui.ColorGreen
v.Frame = false
if err := gui.renderString(g, "information", information); err != nil {
return err
}
// doing this here because it'll only happen once
if err := gui.loadNewDirectory(); err != nil {
return err
}
}
if gui.g.CurrentView() == nil {
if _, err := gui.g.SetCurrentView(gui.getContainersView().Name()); err != nil {
return err
}
if err := gui.switchFocus(gui.g, nil, gui.getServicesView()); err != nil {
return err
}
}
type listViewState struct {
selectedLine int
lineCount int
}
listViews := map[*gocui.View]listViewState{
containersView: {selectedLine: gui.State.Panels.Containers.SelectedLine, lineCount: len(gui.DockerCommand.Containers)},
imagesView: {selectedLine: gui.State.Panels.Images.SelectedLine, lineCount: len(gui.DockerCommand.Images)},
}
// menu view might not exist so we check to be safe
if menuView, err := gui.g.View("menu"); err == nil {
listViews[menuView] = listViewState{selectedLine: gui.State.Panels.Menu.SelectedLine, lineCount: gui.State.MenuItemCount}
}
for view, state := range listViews {
// check if the selected line is now out of view and if so refocus it
if err := gui.focusPoint(0, state.selectedLine, state.lineCount, view); err != nil {
return err
}
}
// here is a good place log some stuff
// if you download humanlog and do tail -f development.log | humanlog
// this will let you see these branches as prettified json
// gui.Log.Info(utils.AsJson(gui.State.Branches[0:4]))
return gui.resizeCurrentPopupPanel(g)
}
func (gui *Gui) loadNewDirectory() error { func (gui *Gui) loadNewDirectory() error {
gui.waitForIntro.Done() gui.waitForIntro.Done()
@ -618,3 +325,11 @@ func (gui *Gui) runSyncOrAsyncCommand(sub *exec.Cmd, err error) (bool, error) {
} }
return true, nil return true, nil
} }
func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error {
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("CustomCommand"), func(g *gocui.Gui, v *gocui.View) error {
command := gui.trimmedContent(v)
gui.SubProcess = gui.OSCommand.RunCustomCommand(command)
return gui.Errors.ErrSubProcess
})
}

View file

@ -76,43 +76,57 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Key: gocui.KeyEsc, Key: gocui.KeyEsc,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.quit, Handler: gui.quit,
}, { },
{
ViewName: "", ViewName: "",
Key: gocui.KeyPgup, Key: gocui.KeyPgup,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.scrollUpMain, Handler: gui.scrollUpMain,
}, { },
{
ViewName: "", ViewName: "",
Key: gocui.KeyPgdn, Key: gocui.KeyPgdn,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.scrollDownMain, Handler: gui.scrollDownMain,
}, { },
{
ViewName: "", ViewName: "",
Key: gocui.KeyCtrlU, Key: gocui.KeyCtrlU,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.scrollUpMain, Handler: gui.scrollUpMain,
}, { },
{
ViewName: "", ViewName: "",
Key: gocui.KeyCtrlD, Key: gocui.KeyCtrlD,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.scrollDownMain, Handler: gui.scrollDownMain,
}, { },
{
ViewName: "", ViewName: "",
Key: gocui.KeyEnd, Key: gocui.KeyEnd,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.autoScrollMain, Handler: gui.autoScrollMain,
}, { },
{
ViewName: "", ViewName: "",
Key: 'x', Key: 'x',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleCreateOptionsMenu, Handler: gui.handleCreateOptionsMenu,
}, { },
{
ViewName: "",
Key: 'X',
Modifier: gocui.ModNone,
Handler: gui.handleCustomCommand,
},
{
ViewName: "status", ViewName: "status",
Key: 'e', Key: 'e',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleEditConfig, Handler: gui.handleEditConfig,
Description: gui.Tr.SLocalize("EditConfig"), Description: gui.Tr.SLocalize("EditConfig"),
}, { },
{
ViewName: "status", ViewName: "status",
Key: 'o', Key: 'o',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
@ -124,23 +138,27 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Key: gocui.KeyEsc, Key: gocui.KeyEsc,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleMenuClose, Handler: gui.handleMenuClose,
}, { },
{
ViewName: "menu", ViewName: "menu",
Key: 'q', Key: 'q',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleMenuClose, Handler: gui.handleMenuClose,
}, { },
{
ViewName: "information", ViewName: "information",
Key: gocui.MouseLeft, Key: gocui.MouseLeft,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleDonate, Handler: gui.handleDonate,
}, { },
{
ViewName: "containers", ViewName: "containers",
Key: '[', Key: '[',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleContainersPrevContext, Handler: gui.handleContainersPrevContext,
Description: gui.Tr.SLocalize("previousContext"), Description: gui.Tr.SLocalize("previousContext"),
}, { },
{
ViewName: "containers", ViewName: "containers",
Key: ']', Key: ']',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
@ -230,7 +248,8 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleImagesPrevContext, Handler: gui.handleImagesPrevContext,
Description: gui.Tr.SLocalize("previousContext"), Description: gui.Tr.SLocalize("previousContext"),
}, { },
{
ViewName: "images", ViewName: "images",
Key: ']', Key: ']',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,

260
pkg/gui/layout.go Normal file
View file

@ -0,0 +1,260 @@
package gui
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/utils"
)
// getFocusLayout returns a manager function for when view gain and lose focus
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
var previousView *gocui.View
return func(g *gocui.Gui) error {
newView := gui.g.CurrentView()
if err := gui.onFocusChange(); err != nil {
return err
}
// for now we don't consider losing focus to a popup panel as actually losing focus
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
if err := gui.onFocusLost(previousView, newView); err != nil {
return err
}
if err := gui.onFocus(newView); err != nil {
return err
}
previousView = newView
}
return nil
}
}
func (gui *Gui) onFocusChange() error {
currentView := gui.g.CurrentView()
for _, view := range gui.g.Views() {
view.Highlight = view == currentView
}
return nil
}
func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
if v == nil {
return nil
}
gui.Log.Info(v.Name() + " focus lost")
return nil
}
func (gui *Gui) onFocus(v *gocui.View) error {
if v == nil {
return nil
}
gui.Log.Info(v.Name() + " focus gained")
return nil
}
// layout is called for every screen re-render e.g. when the screen is resized
func (gui *Gui) layout(g *gocui.Gui) error {
g.Highlight = true
width, height := g.Size()
information := gui.Config.Version
minimumHeight := 9
minimumWidth := 10
if height < minimumHeight || width < minimumWidth {
v, err := g.SetView("limit", 0, 0, width-1, height-1, 0)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
v.Wrap = true
_, _ = g.SetViewOnTop("limit")
}
return nil
}
currView := gui.g.CurrentView()
currentCyclebleView := gui.State.PreviousView
if currView != nil {
viewName := currView.Name()
usePreviouseView := true
for _, view := range cyclableViews {
if view == viewName {
currentCyclebleView = viewName
usePreviouseView = false
break
}
}
if usePreviouseView {
currentCyclebleView = gui.State.PreviousView
}
}
usableSpace := height - 4
tallPanels := 3
vHeights := map[string]int{
"status": tallPanels,
"services": usableSpace/tallPanels + usableSpace%tallPanels,
"containers": usableSpace / tallPanels,
"images": usableSpace / tallPanels,
"options": 1,
}
if height < 28 {
defaultHeight := 3
if height < 21 {
defaultHeight = 1
}
vHeights = map[string]int{
"status": defaultHeight,
"services": defaultHeight,
"containers": defaultHeight,
"images": defaultHeight,
"options": defaultHeight,
}
vHeights[currentCyclebleView] = height - defaultHeight*tallPanels - 1
}
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)
leftSideWidth := width / 3
appStatus := gui.statusManager.getStatusString()
appStatusOptionsBoundary := 0
if appStatus != "" {
appStatusOptionsBoundary = len(appStatus) + 2
}
_, _ = g.SetViewOnBottom("limit")
g.DeleteView("limit")
v, err := g.SetView("main", leftSideWidth+1, 0, width-1, height-2, gocui.LEFT)
if err != nil {
if err.Error() != "unknown view" {
return err
}
v.Wrap = true
v.FgColor = gocui.ColorWhite
}
if v, err := g.SetView("status", 0, 0, leftSideWidth, vHeights["status"]-1, gocui.BOTTOM|gocui.RIGHT); err != nil {
if err.Error() != "unknown view" {
return err
}
v.Title = gui.Tr.SLocalize("StatusTitle")
v.FgColor = gocui.ColorWhite
}
servicesView, err := g.SetViewBeneath("services", "status", vHeights["services"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
servicesView.Highlight = true
servicesView.Title = gui.Tr.SLocalize("ServicesTitle")
servicesView.FgColor = gocui.ColorWhite
gui.focusPoint(0, gui.State.Panels.Services.SelectedLine, len(gui.DockerCommand.Services), servicesView)
}
containersView, err := g.SetViewBeneath("containers", "services", vHeights["containers"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
containersView.Highlight = true
containersView.Title = gui.Tr.SLocalize("ContainersTitle")
containersView.FgColor = gocui.ColorWhite
gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.Containers), containersView)
}
imagesView, err := g.SetViewBeneath("images", "containers", vHeights["images"])
if err != nil {
if err.Error() != "unknown view" {
return err
}
imagesView.Highlight = true
imagesView.Title = gui.Tr.SLocalize("ImagesTitle")
imagesView.FgColor = gocui.ColorWhite
gui.focusPoint(0, gui.State.Panels.Images.SelectedLine, len(gui.DockerCommand.Images), imagesView)
}
if v, err := g.SetView("options", appStatusOptionsBoundary-1, height-2, optionsVersionBoundary-1, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
v.Frame = false
if v.FgColor, err = gui.GetOptionsPanelTextColor(); err != nil {
return err
}
}
if appStatusView, err := g.SetView("appStatus", -1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
appStatusView.BgColor = gocui.ColorDefault
appStatusView.FgColor = gocui.ColorCyan
appStatusView.Frame = false
if _, err := g.SetViewOnBottom("appStatus"); err != nil {
return err
}
}
if v, err := g.SetView("information", optionsVersionBoundary-1, height-2, width, height, 0); err != nil {
if err.Error() != "unknown view" {
return err
}
v.BgColor = gocui.ColorDefault
v.FgColor = gocui.ColorGreen
v.Frame = false
if err := gui.renderString(g, "information", information); err != nil {
return err
}
// doing this here because it'll only happen once
if err := gui.loadNewDirectory(); err != nil {
return err
}
}
if gui.g.CurrentView() == nil {
if _, err := gui.g.SetCurrentView(gui.getContainersView().Name()); err != nil {
return err
}
if err := gui.switchFocus(gui.g, nil, gui.getServicesView()); err != nil {
return err
}
}
type listViewState struct {
selectedLine int
lineCount int
}
listViews := map[*gocui.View]listViewState{
containersView: {selectedLine: gui.State.Panels.Containers.SelectedLine, lineCount: len(gui.DockerCommand.Containers)},
imagesView: {selectedLine: gui.State.Panels.Images.SelectedLine, lineCount: len(gui.DockerCommand.Images)},
}
// menu view might not exist so we check to be safe
if menuView, err := gui.g.View("menu"); err == nil {
listViews[menuView] = listViewState{selectedLine: gui.State.Panels.Menu.SelectedLine, lineCount: gui.State.MenuItemCount}
}
for view, state := range listViews {
// check if the selected line is now out of view and if so refocus it
if err := gui.focusPoint(0, state.selectedLine, state.lineCount, view); err != nil {
return err
}
}
// here is a good place log some stuff
// if you download humanlog and do tail -f development.log | humanlog
// this will let you see these branches as prettified json
// gui.Log.Info(utils.AsJson(gui.State.Branches[0:4]))
return gui.resizeCurrentPopupPanel(g)
}

33
pkg/gui/main_panel.go Normal file
View file

@ -0,0 +1,33 @@
package gui
import (
"math"
"github.com/jesseduffield/gocui"
)
func (gui *Gui) scrollUpMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main")
mainView.Autoscroll = false
ox, oy := mainView.Origin()
newOy := int(math.Max(0, float64(oy-gui.Config.UserConfig.Gui.ScrollHeight)))
return mainView.SetOrigin(ox, newOy)
}
func (gui *Gui) scrollDownMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main")
ox, oy := mainView.Origin()
y := oy
if !gui.Config.UserConfig.Gui.ScrollPastBottom {
_, sy := mainView.Size()
y += sy
}
// for some reason we can't work out whether we've hit the bottomq
// there is a large discrepancy in the origin's y value and the length of BufferLines
return mainView.SetOrigin(ox, oy+gui.Config.UserConfig.Gui.ScrollHeight)
}
func (gui *Gui) autoScrollMain(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Autoscroll = true
return nil
}

View file

@ -62,9 +62,9 @@ func (gui *Gui) runCommand() error {
gui.SubProcess.Stdin = nil gui.SubProcess.Stdin = nil
gui.SubProcess = nil gui.SubProcess = nil
// fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen)) fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen))
// fmt.Scanln() // wait for enter press fmt.Scanln() // wait for enter press
return nil return nil
} }

View file

@ -868,5 +868,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
ID: "StopService", ID: "StopService",
Other: "Are you sure you want to stop this service's containers? (enter/esc)", Other: "Are you sure you want to stop this service's containers? (enter/esc)",
}, },
&i18n.Message{
ID: "pressEnterToReturn",
Other: "Press enter to return to lazydocker",
},
) )
} }

View file

@ -58,7 +58,7 @@ func (t *Task) Stop() {
// NewTickerTask is a convenience function for making a new task that repeats some action once per e.g. second // NewTickerTask is a convenience function for making a new task that repeats some action once per e.g. second
func (t *TaskManager) NewTickerTask(duration time.Duration, f func()) error { func (t *TaskManager) NewTickerTask(duration time.Duration, f func()) error {
return t.NewTask(func(stop chan struct{}) { return t.NewTask(func(stop chan struct{}) {
tickChan := time.NewTicker(time.Second) tickChan := time.NewTicker(duration)
f() // calling f first so that we're not waiting for the first tick f() // calling f first so that we're not waiting for the first tick
for { for {
select { select {