mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-22 07:11:01 +00:00
Focus previously switched the box from Size to Weight: 2, giving the panel ~2/7 of the side area regardless of how many rows it had. That also ignored Gui.ExpandFocusedSidePanel — every other side panel only expands on focus when accordion mode is on. Route the project box through the same accordionBox helper as the others. Stays fitted to content normally; expands on focus only when the user opted in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
232 lines
6.2 KiB
Go
232 lines
6.2 KiB
Go
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"
|
|
)
|
|
|
|
// In this file we use the boxlayout package, along with knowledge about the app's state,
|
|
// to arrange the windows (i.e. panels) on the screen.
|
|
|
|
const INFO_SECTION_PADDING = " "
|
|
|
|
func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
|
|
minimumHeight := 9
|
|
minimumWidth := 10
|
|
width, height := gui.g.Size()
|
|
if width < minimumWidth || height < minimumHeight {
|
|
return boxlayout.ArrangeWindows(&boxlayout.Box{Window: "limit"}, 0, 0, width, height)
|
|
}
|
|
|
|
sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights()
|
|
|
|
sidePanelsDirection := boxlayout.COLUMN
|
|
portraitMode := width <= 84 && height > 45
|
|
if portraitMode {
|
|
sidePanelsDirection = boxlayout.ROW
|
|
}
|
|
|
|
showInfoSection := gui.Config.UserConfig.Gui.ShowBottomLine || gui.State.Filter.active
|
|
infoSectionSize := 0
|
|
if showInfoSection {
|
|
infoSectionSize = 1
|
|
}
|
|
|
|
root := &boxlayout.Box{
|
|
Direction: boxlayout.ROW,
|
|
Children: []*boxlayout.Box{
|
|
{
|
|
Direction: sidePanelsDirection,
|
|
Weight: 1,
|
|
Children: []*boxlayout.Box{
|
|
{
|
|
Direction: boxlayout.ROW,
|
|
Weight: sideSectionWeight,
|
|
ConditionalChildren: gui.sidePanelChildren,
|
|
},
|
|
{
|
|
Window: "main",
|
|
Weight: mainSectionWeight,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Direction: boxlayout.COLUMN,
|
|
Size: infoSectionSize,
|
|
Children: gui.infoSectionChildren(informationStr, appStatus),
|
|
},
|
|
},
|
|
}
|
|
|
|
return boxlayout.ArrangeWindows(root, 0, 0, width, height)
|
|
}
|
|
|
|
func (gui *Gui) getMidSectionWeights() (int, int) {
|
|
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
|
|
// we could make this better by creating ratios like 2:3 rather than always 1:something
|
|
mainSectionWeight := int(1/sidePanelWidthRatio) - 1
|
|
sideSectionWeight := 1
|
|
|
|
if currentWindow == "main" && gui.State.ScreenMode == SCREEN_FULL {
|
|
mainSectionWeight = 1
|
|
sideSectionWeight = 0
|
|
} else {
|
|
if gui.State.ScreenMode == SCREEN_HALF {
|
|
mainSectionWeight = 1
|
|
} else if gui.State.ScreenMode == SCREEN_FULL {
|
|
mainSectionWeight = 0
|
|
}
|
|
}
|
|
|
|
return sideSectionWeight, mainSectionWeight
|
|
}
|
|
|
|
func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*boxlayout.Box {
|
|
result := []*boxlayout.Box{}
|
|
|
|
if len(appStatus) > 0 {
|
|
result = append(result,
|
|
&boxlayout.Box{
|
|
Window: "appStatus",
|
|
Size: runewidth.StringWidth(appStatus) + runewidth.StringWidth(INFO_SECTION_PADDING),
|
|
},
|
|
)
|
|
}
|
|
|
|
if gui.State.Filter.active {
|
|
return append(result, []*boxlayout.Box{
|
|
{
|
|
Window: "filterPrefix",
|
|
Size: runewidth.StringWidth(gui.filterPrompt()),
|
|
},
|
|
{
|
|
Window: "filter",
|
|
Weight: 1,
|
|
},
|
|
}...)
|
|
}
|
|
|
|
result = append(result,
|
|
[]*boxlayout.Box{
|
|
{
|
|
Window: "options",
|
|
Weight: 1,
|
|
},
|
|
{
|
|
Window: "information",
|
|
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
|
|
Size: runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(utils.Decolorise(informationStr)),
|
|
},
|
|
}...,
|
|
)
|
|
|
|
return result
|
|
}
|
|
|
|
func (gui *Gui) sideViewNames() []string {
|
|
visibleSidePanels := lo.Filter(gui.allSidePanels(), func(panel panels.ISideListPanel, _ int) bool {
|
|
return !panel.IsHidden()
|
|
})
|
|
|
|
return lo.Map(visibleSidePanels, func(panel panels.ISideListPanel, _ int) string {
|
|
return panel.GetView().Name()
|
|
})
|
|
}
|
|
|
|
func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
|
currentWindow := gui.currentSideWindowName()
|
|
sideWindowNames := gui.sideViewNames()
|
|
|
|
if gui.State.ScreenMode == SCREEN_FULL || gui.State.ScreenMode == SCREEN_HALF {
|
|
fullHeightBox := func(window string) *boxlayout.Box {
|
|
if window == currentWindow {
|
|
return &boxlayout.Box{
|
|
Window: window,
|
|
Weight: 1,
|
|
}
|
|
} else {
|
|
return &boxlayout.Box{
|
|
Window: window,
|
|
Size: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
return lo.Map(sideWindowNames, func(window string, _ int) *boxlayout.Box {
|
|
return fullHeightBox(window)
|
|
})
|
|
|
|
} else if height >= 28 {
|
|
accordionMode := gui.Config.UserConfig.Gui.ExpandFocusedSidePanel
|
|
accordionBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
|
|
if accordionMode && defaultBox.Window == currentWindow {
|
|
return &boxlayout.Box{
|
|
Window: defaultBox.Window,
|
|
Weight: 2,
|
|
}
|
|
}
|
|
|
|
return defaultBox
|
|
}
|
|
|
|
// The project panel sizes to fit its rows (so profiles are visible
|
|
// without having to click in). Capped so a project with many profiles
|
|
// can't crowd out the other side panels. Routed through accordionBox
|
|
// so it follows the same focus-expansion rule as the other panels
|
|
// (only expands on focus when Gui.ExpandFocusedSidePanel is set).
|
|
// This only applies when the project panel is actually visible
|
|
// (i.e. we are inside a compose project).
|
|
if len(sideWindowNames) > 0 && sideWindowNames[0] == "project" {
|
|
const maxProjectPanelSize = 8
|
|
size := gui.Panels.Projects.List.Len() + 2
|
|
if size < 3 {
|
|
size = 3
|
|
} else if size > maxProjectPanelSize {
|
|
size = maxProjectPanelSize
|
|
}
|
|
projectBox := accordionBox(&boxlayout.Box{
|
|
Window: sideWindowNames[0],
|
|
Size: size,
|
|
})
|
|
|
|
return append([]*boxlayout.Box{
|
|
projectBox,
|
|
}, lo.Map(sideWindowNames[1:], func(window string, _ int) *boxlayout.Box {
|
|
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
|
})...)
|
|
}
|
|
|
|
return lo.Map(sideWindowNames, func(window string, _ int) *boxlayout.Box {
|
|
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
|
})
|
|
} else {
|
|
squashedHeight := 1
|
|
if height >= 21 {
|
|
squashedHeight = 3
|
|
}
|
|
|
|
squashedSidePanelBox := func(window string) *boxlayout.Box {
|
|
if window == currentWindow {
|
|
return &boxlayout.Box{
|
|
Window: window,
|
|
Weight: 1,
|
|
}
|
|
} else {
|
|
return &boxlayout.Box{
|
|
Window: window,
|
|
Size: squashedHeight,
|
|
}
|
|
}
|
|
}
|
|
|
|
return lo.Map(sideWindowNames, func(window string, _ int) *boxlayout.Box {
|
|
return squashedSidePanelBox(window)
|
|
})
|
|
}
|
|
}
|