mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
more refactoring of list panels
This commit is contained in:
parent
5d498c796a
commit
fc592b5806
18 changed files with 239 additions and 221 deletions
|
|
@ -2,6 +2,7 @@ package gui
|
|||
|
||||
import (
|
||||
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/samber/lo"
|
||||
|
|
@ -129,12 +130,12 @@ func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*
|
|||
}
|
||||
|
||||
func (gui *Gui) sideViewNames() []string {
|
||||
visibleSidePanels := lo.Filter(gui.allSidePanels(), func(panel ISideListPanel, _ int) bool {
|
||||
visibleSidePanels := lo.Filter(gui.allSidePanels(), func(panel panels.ISideListPanel, _ int) bool {
|
||||
return !panel.IsHidden()
|
||||
})
|
||||
|
||||
return lo.Map(visibleSidePanels, func(panel ISideListPanel, _ int) string {
|
||||
return panel.View().Name()
|
||||
return lo.Map(visibleSidePanels, func(panel panels.ISideListPanel, _ int) string {
|
||||
return panel.GetView().Name()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ import (
|
|||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getContainersPanel() *SideListPanel[*commands.Container] {
|
||||
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] {
|
||||
// Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context.
|
||||
isStandaloneContainer := func(container *commands.Container) bool {
|
||||
if container.OneOff || container.ServiceName == "" {
|
||||
|
|
@ -27,34 +28,34 @@ func (gui *Gui) getContainersPanel() *SideListPanel[*commands.Container] {
|
|||
})
|
||||
}
|
||||
|
||||
return &SideListPanel[*commands.Container]{
|
||||
ContextState: &ContextState[*commands.Container]{
|
||||
GetContexts: func() []ContextConfig[*commands.Container] {
|
||||
return []ContextConfig[*commands.Container]{
|
||||
return &panels.SideListPanel[*commands.Container]{
|
||||
ContextState: &panels.ContextState[*commands.Container]{
|
||||
GetContexts: func() []panels.ContextConfig[*commands.Container] {
|
||||
return []panels.ContextConfig[*commands.Container]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderContainerLogsToMain,
|
||||
Key: "logs",
|
||||
Title: gui.Tr.LogsTitle,
|
||||
Render: gui.renderContainerLogsToMain,
|
||||
},
|
||||
{
|
||||
key: "stats",
|
||||
title: gui.Tr.StatsTitle,
|
||||
render: gui.renderContainerStats,
|
||||
Key: "stats",
|
||||
Title: gui.Tr.StatsTitle,
|
||||
Render: gui.renderContainerStats,
|
||||
},
|
||||
{
|
||||
key: "env",
|
||||
title: gui.Tr.EnvTitle,
|
||||
render: gui.renderContainerEnv,
|
||||
Key: "env",
|
||||
Title: gui.Tr.EnvTitle,
|
||||
Render: gui.renderContainerEnv,
|
||||
},
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: gui.renderContainerConfig,
|
||||
Key: "config",
|
||||
Title: gui.Tr.ConfigTitle,
|
||||
Render: gui.renderContainerConfig,
|
||||
},
|
||||
{
|
||||
key: "top",
|
||||
title: gui.Tr.TopTitle,
|
||||
render: gui.renderContainerTop,
|
||||
Key: "top",
|
||||
Title: gui.Tr.TopTitle,
|
||||
Render: gui.renderContainerTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
@ -62,12 +63,12 @@ func (gui *Gui) getContainersPanel() *SideListPanel[*commands.Container] {
|
|||
return "containers-" + container.ID + "-" + container.Container.State
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Container]{
|
||||
List: NewFilteredList[*commands.Container](),
|
||||
view: gui.Views.Containers,
|
||||
ListPanel: panels.ListPanel[*commands.Container]{
|
||||
List: panels.NewFilteredList[*commands.Container](),
|
||||
View: gui.Views.Containers,
|
||||
},
|
||||
NoItemsMessage: gui.Tr.NoContainers,
|
||||
gui: gui.intoInterface(),
|
||||
Gui: gui.intoInterface(),
|
||||
// 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 {
|
||||
|
|
@ -282,7 +283,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
|||
if i == originalSelectedLineIdx {
|
||||
break
|
||||
}
|
||||
gui.Panels.Services.setSelectedLineIdx(i)
|
||||
gui.Panels.Services.SetSelectedLineIdx(i)
|
||||
gui.Panels.Services.Refocus()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ func (gui *Gui) handleOpenFilter() error {
|
|||
|
||||
func (gui *Gui) onNewFilterNeedle(value string) error {
|
||||
gui.State.Filter.needle = value
|
||||
gui.ResetOrigin(gui.State.Filter.panel.View())
|
||||
gui.ResetOrigin(gui.State.Filter.panel.GetView())
|
||||
return gui.State.Filter.panel.RerenderList()
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ func (gui *Gui) clearFilter() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
gui.ResetOrigin(panel.View())
|
||||
gui.ResetOrigin(panel.GetView())
|
||||
|
||||
return panel.RerenderList()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func (gui *Gui) switchFocusAux(newView *gocui.View) error {
|
|||
|
||||
newViewStack := gui.State.ViewStack
|
||||
|
||||
if gui.State.Filter.panel != nil && !lo.Contains(newViewStack, gui.State.Filter.panel.View().Name()) {
|
||||
if gui.State.Filter.panel != nil && !lo.Contains(newViewStack, gui.State.Filter.panel.GetView().Name()) {
|
||||
if err := gui.clearFilter(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
lcUtils "github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
||||
"github.com/jesseduffield/lazydocker/pkg/tasks"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
|
|
@ -49,12 +50,12 @@ type Gui struct {
|
|||
}
|
||||
|
||||
type Panels struct {
|
||||
Projects *SideListPanel[*commands.Project]
|
||||
Services *SideListPanel[*commands.Service]
|
||||
Containers *SideListPanel[*commands.Container]
|
||||
Images *SideListPanel[*commands.Image]
|
||||
Volumes *SideListPanel[*commands.Volume]
|
||||
Menu *SideListPanel[*MenuItem]
|
||||
Projects *panels.SideListPanel[*commands.Project]
|
||||
Services *panels.SideListPanel[*commands.Service]
|
||||
Containers *panels.SideListPanel[*commands.Container]
|
||||
Images *panels.SideListPanel[*commands.Image]
|
||||
Volumes *panels.SideListPanel[*commands.Volume]
|
||||
Menu *panels.SideListPanel[*MenuItem]
|
||||
}
|
||||
|
||||
type Mutexes struct {
|
||||
|
|
@ -94,7 +95,7 @@ type filterState struct {
|
|||
// or we've committed the filter and we're back in the list view
|
||||
active bool
|
||||
// The panel that we're filtering.
|
||||
panel ISideListPanel
|
||||
panel panels.ISideListPanel
|
||||
// The string that we're filtering on
|
||||
needle string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,22 @@ import (
|
|||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getImagesPanel() *SideListPanel[*commands.Image] {
|
||||
func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] {
|
||||
noneLabel := "<none>"
|
||||
|
||||
return &SideListPanel[*commands.Image]{
|
||||
ContextState: &ContextState[*commands.Image]{
|
||||
GetContexts: func() []ContextConfig[*commands.Image] {
|
||||
return []ContextConfig[*commands.Image]{
|
||||
return &panels.SideListPanel[*commands.Image]{
|
||||
ContextState: &panels.ContextState[*commands.Image]{
|
||||
GetContexts: func() []panels.ContextConfig[*commands.Image] {
|
||||
return []panels.ContextConfig[*commands.Image]{
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: func(image *commands.Image) error {
|
||||
Key: "config",
|
||||
Title: gui.Tr.ConfigTitle,
|
||||
Render: func(image *commands.Image) error {
|
||||
return gui.renderImageConfig(image)
|
||||
},
|
||||
},
|
||||
|
|
@ -34,12 +35,12 @@ func (gui *Gui) getImagesPanel() *SideListPanel[*commands.Image] {
|
|||
return "images-" + image.ID
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Image]{
|
||||
List: NewFilteredList[*commands.Image](),
|
||||
view: gui.Views.Images,
|
||||
ListPanel: panels.ListPanel[*commands.Image]{
|
||||
List: panels.NewFilteredList[*commands.Image](),
|
||||
View: gui.Views.Images,
|
||||
},
|
||||
NoItemsMessage: gui.Tr.NoImages,
|
||||
gui: gui.intoInterface(),
|
||||
Gui: gui.intoInterface(),
|
||||
Sort: func(a *commands.Image, b *commands.Image) bool {
|
||||
if a.Name == noneLabel && b.Name != noneLabel {
|
||||
return false
|
||||
|
|
@ -114,7 +115,7 @@ func (gui *Gui) refreshStateImages() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) FilterString(view *gocui.View) string {
|
||||
if gui.State.Filter.panel != nil && gui.State.Filter.panel.View() != view {
|
||||
if gui.State.Filter.panel != nil && gui.State.Filter.panel.GetView() != view {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -492,12 +492,12 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
|
||||
for _, panel := range gui.allSidePanels() {
|
||||
bindings = append(bindings, []*Binding{
|
||||
{ViewName: panel.View().Name(), Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: panel.View().Name(), Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: panel.View().Name(), Key: 'h', Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: panel.View().Name(), Key: 'l', Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: panel.View().Name(), Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: panel.View().Name(), Key: gocui.KeyBacktab, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: panel.GetView().Name(), Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: panel.GetView().Name(), Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: panel.GetView().Name(), Key: 'h', Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: panel.GetView().Name(), Key: 'l', Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: panel.GetView().Name(), Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: panel.GetView().Name(), Key: gocui.KeyBacktab, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
}...)
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +514,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
}
|
||||
|
||||
for _, panel := range gui.allListPanels() {
|
||||
setUpDownClickBindings(panel.View().Name(), panel.HandlePrevLine, panel.HandleNextLine, panel.HandleClick)
|
||||
setUpDownClickBindings(panel.GetView().Name(), panel.HandlePrevLine, panel.HandleNextLine, panel.HandleClick)
|
||||
}
|
||||
|
||||
setUpDownClickBindings("main", gui.scrollUpMain, gui.scrollDownMain, gui.handleMainClick)
|
||||
|
|
@ -522,21 +522,21 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
for _, panel := range gui.allSidePanels() {
|
||||
bindings = append(bindings,
|
||||
&Binding{
|
||||
ViewName: panel.View().Name(),
|
||||
ViewName: panel.GetView().Name(),
|
||||
Key: gocui.KeyEnter,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleEnterMain,
|
||||
Description: gui.Tr.FocusMain,
|
||||
},
|
||||
&Binding{
|
||||
ViewName: panel.View().Name(),
|
||||
ViewName: panel.GetView().Name(),
|
||||
Key: '[',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: wrappedHandler(panel.HandlePrevContext),
|
||||
Description: gui.Tr.PreviousContext,
|
||||
},
|
||||
&Binding{
|
||||
ViewName: panel.View().Name(),
|
||||
ViewName: panel.GetView().Name(),
|
||||
Key: ']',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: wrappedHandler(panel.HandleNextContext),
|
||||
|
|
@ -547,7 +547,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
|
||||
for _, panel := range gui.allListPanels() {
|
||||
bindings = append(bindings, &Binding{
|
||||
ViewName: panel.View().Name(),
|
||||
ViewName: panel.GetView().Name(),
|
||||
Key: '/',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: wrappedHandler(gui.handleOpenFilter),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
)
|
||||
|
||||
|
|
@ -22,14 +23,14 @@ type CreateMenuOptions struct {
|
|||
HideCancel bool
|
||||
}
|
||||
|
||||
func (gui *Gui) getMenuPanel() *SideListPanel[*MenuItem] {
|
||||
return &SideListPanel[*MenuItem]{
|
||||
ListPanel: ListPanel[*MenuItem]{
|
||||
List: NewFilteredList[*MenuItem](),
|
||||
view: gui.Views.Menu,
|
||||
func (gui *Gui) getMenuPanel() *panels.SideListPanel[*MenuItem] {
|
||||
return &panels.SideListPanel[*MenuItem]{
|
||||
ListPanel: panels.ListPanel[*MenuItem]{
|
||||
List: panels.NewFilteredList[*MenuItem](),
|
||||
View: gui.Views.Menu,
|
||||
},
|
||||
NoItemsMessage: "",
|
||||
gui: gui.intoInterface(),
|
||||
Gui: gui.intoInterface(),
|
||||
OnClick: gui.onMenuPress,
|
||||
Sort: nil,
|
||||
GetDisplayStrings: func(menuItem *MenuItem) []string {
|
||||
|
|
@ -101,7 +102,7 @@ func (gui *Gui) Menu(opts CreateMenuOptions) error {
|
|||
}
|
||||
|
||||
gui.Panels.Menu.SetItems(opts.Items)
|
||||
gui.Panels.Menu.setSelectedLineIdx(0)
|
||||
gui.Panels.Menu.SetSelectedLineIdx(0)
|
||||
|
||||
if err := gui.Panels.Menu.RerenderList(); err != nil {
|
||||
return err
|
||||
|
|
|
|||
7
pkg/gui/panels.go
Normal file
7
pkg/gui/panels.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package gui
|
||||
|
||||
import "github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
|
||||
func (gui *Gui) intoInterface() panels.IGui {
|
||||
return gui
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package gui
|
||||
package panels
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package gui
|
||||
package panels
|
||||
|
||||
import (
|
||||
"testing"
|
||||
42
pkg/gui/panels/list_panel.go
Normal file
42
pkg/gui/panels/list_panel.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package panels
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
lcUtils "github.com/jesseduffield/lazycore/pkg/utils"
|
||||
)
|
||||
|
||||
type ListPanel[T comparable] struct {
|
||||
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)
|
||||
}
|
||||
|
||||
self.SelectedIdx = clampedValue
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) clampSelectedLineIdx() {
|
||||
clamped := lcUtils.Clamp(self.SelectedIdx, 0, self.List.Len()-1)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) SelectNextLine() {
|
||||
self.moveSelectedLine(1)
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) SelectPrevLine() {
|
||||
self.moveSelectedLine(-1)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package gui
|
||||
package panels
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -6,53 +6,23 @@ import (
|
|||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
lcUtils "github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type ListPanel[T comparable] struct {
|
||||
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)
|
||||
}
|
||||
|
||||
self.SelectedIdx = clampedValue
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) clampSelectedLineIdx() {
|
||||
clamped := lcUtils.Clamp(self.SelectedIdx, 0, self.List.Len()-1)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (self *ListPanel[T]) SelectNextLine() {
|
||||
self.moveSelectedLine(1)
|
||||
}
|
||||
|
||||
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
|
||||
type ISideListPanel interface {
|
||||
SetContextIndex(int)
|
||||
HandleSelect() error
|
||||
GetView() *gocui.View
|
||||
Refocus()
|
||||
RerenderList() error
|
||||
IsFilterDisabled() bool
|
||||
IsHidden() bool
|
||||
HandleNextLine() error
|
||||
HandlePrevLine() error
|
||||
HandleClick() error
|
||||
HandlePrevContext() error
|
||||
HandleNextContext() error
|
||||
}
|
||||
|
||||
// list panel at the side of the screen that renders content to the main panel
|
||||
|
|
@ -65,7 +35,7 @@ type SideListPanel[T comparable] struct {
|
|||
// and it has focus. Leave empty if you don't want to render anything
|
||||
NoItemsMessage string
|
||||
|
||||
gui IGui
|
||||
Gui IGui
|
||||
|
||||
// this Filter is applied on top of additional default filters
|
||||
Filter func(T) bool
|
||||
|
|
@ -85,27 +55,20 @@ type SideListPanel[T comparable] struct {
|
|||
Hide func() bool
|
||||
}
|
||||
|
||||
type ISideListPanel interface {
|
||||
SetContextIndex(int)
|
||||
HandleSelect() error
|
||||
View() *gocui.View
|
||||
Refocus()
|
||||
RerenderList() error
|
||||
IsFilterDisabled() bool
|
||||
IsHidden() bool
|
||||
HandleNextLine() error
|
||||
HandlePrevLine() error
|
||||
HandleClick() error
|
||||
HandlePrevContext() error
|
||||
HandleNextContext() error
|
||||
}
|
||||
|
||||
var _ ISideListPanel = &SideListPanel[int]{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type ContextConfig[T any] struct {
|
||||
key string
|
||||
title string
|
||||
render func(item T) error
|
||||
Key string
|
||||
Title string
|
||||
Render func(item T) error
|
||||
}
|
||||
|
||||
type IGui interface {
|
||||
|
|
@ -121,16 +84,12 @@ type IGui interface {
|
|||
Update(func() error)
|
||||
}
|
||||
|
||||
func (gui *Gui) intoInterface() IGui {
|
||||
return gui
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) HandleClick() error {
|
||||
itemCount := self.List.Len()
|
||||
handleSelect := self.HandleSelect
|
||||
selectedLine := &self.SelectedIdx
|
||||
|
||||
if err := self.gui.HandleClick(self.view, itemCount, selectedLine, handleSelect); err != nil {
|
||||
if err := self.Gui.HandleClick(self.View, itemCount, selectedLine, handleSelect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -144,8 +103,8 @@ func (self *SideListPanel[T]) HandleClick() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) View() *gocui.View {
|
||||
return self.view
|
||||
func (self *SideListPanel[T]) GetView() *gocui.View {
|
||||
return self.View
|
||||
}
|
||||
|
||||
func (self *SideListPanel[T]) HandleSelect() error {
|
||||
|
|
@ -156,7 +115,7 @@ func (self *SideListPanel[T]) HandleSelect() error {
|
|||
}
|
||||
|
||||
if self.NoItemsMessage != "" {
|
||||
return self.gui.RenderStringMain(self.NoItemsMessage)
|
||||
return self.Gui.RenderStringMain(self.NoItemsMessage)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -173,25 +132,25 @@ func (self *SideListPanel[T]) renderContext(item T) error {
|
|||
}
|
||||
|
||||
key := self.ContextState.GetCurrentContextKey(item)
|
||||
if !self.gui.ShouldRefresh(key) {
|
||||
if !self.Gui.ShouldRefresh(key) {
|
||||
return nil
|
||||
}
|
||||
|
||||
mainView := self.gui.GetMainView()
|
||||
mainView := self.Gui.GetMainView()
|
||||
mainView.Tabs = self.ContextState.GetContextTitles()
|
||||
mainView.TabIndex = self.ContextState.contextIdx
|
||||
|
||||
return self.ContextState.GetCurrentContext().render(item)
|
||||
return self.ContextState.GetCurrentContext().Render(item)
|
||||
}
|
||||
|
||||
func (self *ContextState[T]) GetContextTitles() []string {
|
||||
return lo.Map(self.GetContexts(), func(context ContextConfig[T], _ int) string {
|
||||
return context.title
|
||||
return context.Title
|
||||
})
|
||||
}
|
||||
|
||||
func (self *ContextState[T]) GetCurrentContextKey(item T) string {
|
||||
return self.GetContextCacheKey(item) + "-" + self.GetCurrentContext().key
|
||||
return self.GetContextCacheKey(item) + "-" + self.GetCurrentContext().Key
|
||||
}
|
||||
|
||||
func (self *ContextState[T]) GetCurrentContext() ContextConfig[T] {
|
||||
|
|
@ -263,7 +222,7 @@ func (self *SideListPanel[T]) HandlePrevContext() error {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -272,14 +231,14 @@ func (self *SideListPanel[T]) SetItems(items []T) {
|
|||
}
|
||||
|
||||
func (self *SideListPanel[T]) FilterAndSort() {
|
||||
filterString := self.gui.FilterString(self.view)
|
||||
filterString := self.Gui.FilterString(self.View)
|
||||
|
||||
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 {
|
||||
if lo.SomeBy(self.Gui.IgnoreStrings(), func(ignore string) bool {
|
||||
return lo.SomeBy(self.GetDisplayStrings(item), func(searchString string) bool {
|
||||
return strings.Contains(searchString, ignore)
|
||||
})
|
||||
|
|
@ -304,8 +263,8 @@ func (self *SideListPanel[T]) FilterAndSort() {
|
|||
func (self *SideListPanel[T]) RerenderList() error {
|
||||
self.FilterAndSort()
|
||||
|
||||
self.gui.Update(func() error {
|
||||
self.view.Clear()
|
||||
self.Gui.Update(func() error {
|
||||
self.View.Clear()
|
||||
table := lo.Map(self.List.GetItems(), func(item T, index int) []string {
|
||||
return self.GetDisplayStrings(item)
|
||||
})
|
||||
|
|
@ -313,7 +272,7 @@ func (self *SideListPanel[T]) RerenderList() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(self.view, renderedTable)
|
||||
fmt.Fprint(self.View, renderedTable)
|
||||
|
||||
if self.OnRerender != nil {
|
||||
if err := self.OnRerender(); err != nil {
|
||||
|
|
@ -321,7 +280,7 @@ func (self *SideListPanel[T]) RerenderList() error {
|
|||
}
|
||||
}
|
||||
|
||||
if self.view == self.gui.CurrentView() {
|
||||
if self.View == self.Gui.CurrentView() {
|
||||
return self.HandleSelect()
|
||||
}
|
||||
return nil
|
||||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/jesseduffield/yaml"
|
||||
)
|
||||
|
|
@ -15,35 +16,35 @@ import (
|
|||
// Although at the moment we'll only have one project, in future we could have
|
||||
// a list of projects in the project panel.
|
||||
|
||||
func (gui *Gui) getProjectPanel() *SideListPanel[*commands.Project] {
|
||||
return &SideListPanel[*commands.Project]{
|
||||
ContextState: &ContextState[*commands.Project]{
|
||||
GetContexts: func() []ContextConfig[*commands.Project] {
|
||||
func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||
return &panels.SideListPanel[*commands.Project]{
|
||||
ContextState: &panels.ContextState[*commands.Project]{
|
||||
GetContexts: func() []panels.ContextConfig[*commands.Project] {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
return []ContextConfig[*commands.Project]{
|
||||
return []panels.ContextConfig[*commands.Project]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderAllLogs,
|
||||
Key: "logs",
|
||||
Title: gui.Tr.LogsTitle,
|
||||
Render: gui.renderAllLogs,
|
||||
},
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.DockerComposeConfigTitle,
|
||||
render: gui.renderDockerComposeConfig,
|
||||
Key: "config",
|
||||
Title: gui.Tr.DockerComposeConfigTitle,
|
||||
Render: gui.renderDockerComposeConfig,
|
||||
},
|
||||
{
|
||||
key: "credits",
|
||||
title: gui.Tr.CreditsTitle,
|
||||
render: gui.renderCredits,
|
||||
Key: "credits",
|
||||
Title: gui.Tr.CreditsTitle,
|
||||
Render: gui.renderCredits,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return []ContextConfig[*commands.Project]{
|
||||
return []panels.ContextConfig[*commands.Project]{
|
||||
{
|
||||
key: "credits",
|
||||
title: gui.Tr.CreditsTitle,
|
||||
render: gui.renderCredits,
|
||||
Key: "credits",
|
||||
Title: gui.Tr.CreditsTitle,
|
||||
Render: gui.renderCredits,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
@ -52,12 +53,12 @@ func (gui *Gui) getProjectPanel() *SideListPanel[*commands.Project] {
|
|||
},
|
||||
},
|
||||
|
||||
ListPanel: ListPanel[*commands.Project]{
|
||||
List: NewFilteredList[*commands.Project](),
|
||||
view: gui.Views.Project,
|
||||
ListPanel: panels.ListPanel[*commands.Project]{
|
||||
List: panels.NewFilteredList[*commands.Project](),
|
||||
View: gui.Views.Project,
|
||||
},
|
||||
NoItemsMessage: "",
|
||||
gui: gui.intoInterface(),
|
||||
Gui: gui.intoInterface(),
|
||||
|
||||
Sort: func(a *commands.Project, b *commands.Project) bool {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -8,39 +8,40 @@ import (
|
|||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getServicesPanel() *SideListPanel[*commands.Service] {
|
||||
return &SideListPanel[*commands.Service]{
|
||||
ContextState: &ContextState[*commands.Service]{
|
||||
GetContexts: func() []ContextConfig[*commands.Service] {
|
||||
return []ContextConfig[*commands.Service]{
|
||||
func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
|
||||
return &panels.SideListPanel[*commands.Service]{
|
||||
ContextState: &panels.ContextState[*commands.Service]{
|
||||
GetContexts: func() []panels.ContextConfig[*commands.Service] {
|
||||
return []panels.ContextConfig[*commands.Service]{
|
||||
{
|
||||
key: "logs",
|
||||
title: gui.Tr.LogsTitle,
|
||||
render: gui.renderServiceLogs,
|
||||
Key: "logs",
|
||||
Title: gui.Tr.LogsTitle,
|
||||
Render: gui.renderServiceLogs,
|
||||
},
|
||||
{
|
||||
key: "stats",
|
||||
title: gui.Tr.StatsTitle,
|
||||
render: gui.renderServiceStats,
|
||||
Key: "stats",
|
||||
Title: gui.Tr.StatsTitle,
|
||||
Render: gui.renderServiceStats,
|
||||
},
|
||||
{
|
||||
key: "container-env",
|
||||
title: gui.Tr.ContainerEnvTitle,
|
||||
render: gui.renderServiceContainerEnv,
|
||||
Key: "container-env",
|
||||
Title: gui.Tr.ContainerEnvTitle,
|
||||
Render: gui.renderServiceContainerEnv,
|
||||
},
|
||||
{
|
||||
key: "container-config",
|
||||
title: gui.Tr.ContainerConfigTitle,
|
||||
render: gui.renderServiceContainerConfig,
|
||||
Key: "container-config",
|
||||
Title: gui.Tr.ContainerConfigTitle,
|
||||
Render: gui.renderServiceContainerConfig,
|
||||
},
|
||||
{
|
||||
key: "top",
|
||||
title: gui.Tr.TopTitle,
|
||||
render: gui.renderServiceTop,
|
||||
Key: "top",
|
||||
Title: gui.Tr.TopTitle,
|
||||
Render: gui.renderServiceTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
@ -51,13 +52,12 @@ func (gui *Gui) getServicesPanel() *SideListPanel[*commands.Service] {
|
|||
return "services-" + service.ID + "-" + service.Container.ID + "-" + service.Container.Container.State
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Service]{
|
||||
List: NewFilteredList[*commands.Service](),
|
||||
view: gui.Views.Services,
|
||||
ListPanel: panels.ListPanel[*commands.Service]{
|
||||
List: panels.NewFilteredList[*commands.Service](),
|
||||
View: gui.Views.Services,
|
||||
},
|
||||
// TODO: i18n
|
||||
NoItemsMessage: "no service selected",
|
||||
gui: gui.intoInterface(),
|
||||
NoItemsMessage: gui.Tr.NoServices,
|
||||
Gui: gui.intoInterface(),
|
||||
// sort services first by whether they have a linked container, and second by alphabetical order
|
||||
Sort: func(a *commands.Service, b *commands.Service) bool {
|
||||
if a.Container != nil && b.Container == nil {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spkg/bom"
|
||||
|
|
@ -329,11 +330,11 @@ func (gui *Gui) CurrentView() *gocui.View {
|
|||
return gui.g.CurrentView()
|
||||
}
|
||||
|
||||
func (gui *Gui) currentSidePanel() (ISideListPanel, bool) {
|
||||
func (gui *Gui) currentSidePanel() (panels.ISideListPanel, bool) {
|
||||
viewName := gui.currentViewName()
|
||||
|
||||
for _, sidePanel := range gui.allSidePanels() {
|
||||
if sidePanel.View().Name() == viewName {
|
||||
if sidePanel.GetView().Name() == viewName {
|
||||
return sidePanel, true
|
||||
}
|
||||
}
|
||||
|
|
@ -342,11 +343,11 @@ func (gui *Gui) currentSidePanel() (ISideListPanel, bool) {
|
|||
}
|
||||
|
||||
// returns the current list panel. If no list panel is focused, returns false.
|
||||
func (gui *Gui) currentListPanel() (ISideListPanel, bool) {
|
||||
func (gui *Gui) currentListPanel() (panels.ISideListPanel, bool) {
|
||||
viewName := gui.currentViewName()
|
||||
|
||||
for _, sidePanel := range gui.allListPanels() {
|
||||
if sidePanel.View().Name() == viewName {
|
||||
if sidePanel.GetView().Name() == viewName {
|
||||
return sidePanel, true
|
||||
}
|
||||
}
|
||||
|
|
@ -354,8 +355,8 @@ func (gui *Gui) currentListPanel() (ISideListPanel, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
func (gui *Gui) allSidePanels() []ISideListPanel {
|
||||
return []ISideListPanel{
|
||||
func (gui *Gui) allSidePanels() []panels.ISideListPanel {
|
||||
return []panels.ISideListPanel{
|
||||
gui.Panels.Projects,
|
||||
gui.Panels.Services,
|
||||
gui.Panels.Containers,
|
||||
|
|
@ -364,6 +365,6 @@ func (gui *Gui) allSidePanels() []ISideListPanel {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) allListPanels() []ISideListPanel {
|
||||
func (gui *Gui) allListPanels() []panels.ISideListPanel {
|
||||
return append(gui.allSidePanels(), gui.Panels.Menu)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,20 @@ import (
|
|||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getVolumesPanel() *SideListPanel[*commands.Volume] {
|
||||
return &SideListPanel[*commands.Volume]{
|
||||
ContextState: &ContextState[*commands.Volume]{
|
||||
GetContexts: func() []ContextConfig[*commands.Volume] {
|
||||
return []ContextConfig[*commands.Volume]{
|
||||
func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] {
|
||||
return &panels.SideListPanel[*commands.Volume]{
|
||||
ContextState: &panels.ContextState[*commands.Volume]{
|
||||
GetContexts: func() []panels.ContextConfig[*commands.Volume] {
|
||||
return []panels.ContextConfig[*commands.Volume]{
|
||||
{
|
||||
key: "config",
|
||||
title: gui.Tr.ConfigTitle,
|
||||
render: gui.renderVolumeConfig,
|
||||
Key: "config",
|
||||
Title: gui.Tr.ConfigTitle,
|
||||
Render: gui.renderVolumeConfig,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
@ -27,12 +28,12 @@ func (gui *Gui) getVolumesPanel() *SideListPanel[*commands.Volume] {
|
|||
return "volumes-" + volume.Name
|
||||
},
|
||||
},
|
||||
ListPanel: ListPanel[*commands.Volume]{
|
||||
List: NewFilteredList[*commands.Volume](),
|
||||
view: gui.Views.Volumes,
|
||||
ListPanel: panels.ListPanel[*commands.Volume]{
|
||||
List: panels.NewFilteredList[*commands.Volume](),
|
||||
View: gui.Views.Volumes,
|
||||
},
|
||||
NoItemsMessage: gui.Tr.NoVolumes,
|
||||
gui: gui.intoInterface(),
|
||||
Gui: gui.intoInterface(),
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ type TranslationSet struct {
|
|||
NoContainer string
|
||||
NoImages string
|
||||
NoVolumes string
|
||||
NoServices string
|
||||
RemoveImage string
|
||||
RemoveVolume string
|
||||
RemoveWithoutPrune string
|
||||
|
|
@ -225,6 +226,7 @@ func englishSet() TranslationSet {
|
|||
NoContainer: "No container",
|
||||
NoImages: "No images",
|
||||
NoVolumes: "No volumes",
|
||||
NoServices: "No services",
|
||||
|
||||
ConfirmQuit: "Are you sure you want to quit?",
|
||||
ConfirmUpProject: "Are you sure you want to 'up' your docker compose project?",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue