1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-14 11:23:09 +02:00
lazygit/pkg/gui/layout.go

284 lines
7.7 KiB
Go
Raw Normal View History

2020-03-29 01:31:34 +02:00
package gui
import (
"github.com/jesseduffield/generics/slices"
2020-03-29 01:31:34 +02:00
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
2023-03-23 13:41:24 +02:00
"github.com/jesseduffield/lazygit/pkg/gui/types"
2020-03-29 01:31:34 +02:00
"github.com/jesseduffield/lazygit/pkg/theme"
)
// layout is called for every screen re-render e.g. when the screen is resized
func (gui *Gui) layout(g *gocui.Gui) error {
2021-04-08 15:55:18 +02:00
if !gui.ViewsSetup {
2022-02-05 07:56:36 +02:00
gui.printCommandLogHeader()
if _, err := gui.g.SetCurrentView(gui.defaultSideContext().GetViewName()); err != nil {
2021-04-08 15:55:18 +02:00
return err
}
}
2020-03-29 01:31:34 +02:00
g.Highlight = true
width, height := g.Size()
informationStr := gui.informationStr()
2023-03-23 09:47:29 +02:00
appStatus := gui.helpers.AppStatus.GetStatusString()
2020-08-21 11:53:45 +02:00
viewDimensions := gui.getWindowDimensions(informationStr, appStatus)
2020-03-29 01:31:34 +02:00
// reading more lines into main view buffers upon resize
2021-04-04 16:31:52 +02:00
prevMainView := gui.Views.Main
if prevMainView != nil {
2020-03-29 01:31:34 +02:00
_, prevMainHeight := prevMainView.Size()
newMainHeight := viewDimensions["main"].Y1 - viewDimensions["main"].Y0 - 1
2020-05-16 04:35:19 +02:00
heightDiff := newMainHeight - prevMainHeight
2020-03-29 01:31:34 +02:00
if heightDiff > 0 {
if manager, ok := gui.viewBufferManagerMap["main"]; ok {
manager.ReadLines(heightDiff)
}
if manager, ok := gui.viewBufferManagerMap["secondary"]; ok {
manager.ReadLines(heightDiff)
}
}
}
// we assume that the view has already been created.
setViewFromDimensions := func(viewName string, windowName string) (*gocui.View, error) {
dimensionsObj, ok := viewDimensions[windowName]
view, err := g.View(viewName)
if err != nil {
return nil, err
}
if !ok {
// view not specified in dimensions object: so create the view and hide it
2020-08-23 03:53:05 +02:00
// making the view take up the whole space in the background in case it needs
// to render content as soon as it appears, because lazyloaded content (via a pty task)
// cares about the size of the view.
_, err := g.SetView(viewName, 0, 0, width, height, 0)
view.Visible = false
2021-04-04 15:51:59 +02:00
return view, err
}
frameOffset := 1
if view.Frame {
frameOffset = 0
}
_, err = g.SetView(
2020-05-16 04:35:19 +02:00
viewName,
dimensionsObj.X0-frameOffset,
dimensionsObj.Y0-frameOffset,
dimensionsObj.X1+frameOffset,
dimensionsObj.Y1+frameOffset,
2020-05-16 04:35:19 +02:00
0,
)
view.Visible = true
2021-04-04 15:51:59 +02:00
return view, err
2020-05-16 04:35:19 +02:00
}
for _, context := range gui.State.Contexts.Flatten() {
if !context.HasControlledBounds() {
continue
}
_, err := setViewFromDimensions(context.GetViewName(), context.GetWindowName())
2023-02-10 14:23:48 +02:00
if err != nil && !gocui.IsUnknownView(err) {
2020-03-29 01:31:34 +02:00
return err
}
}
2022-05-08 03:41:13 +02:00
minimumHeight := 9
minimumWidth := 10
gui.Views.Limit.Visible = height < minimumHeight || width < minimumWidth
gui.Views.Tooltip.Visible = gui.Views.Menu.Visible && gui.Views.Tooltip.Buffer() != ""
2023-03-23 13:41:24 +02:00
for _, context := range gui.transientContexts() {
view, err := gui.g.View(context.GetViewName())
2023-02-10 14:23:48 +02:00
if err != nil && !gocui.IsUnknownView(err) {
return err
}
view.Visible = gui.helpers.Window.GetViewNameForWindow(context.GetWindowName()) == context.GetViewName()
}
2020-03-29 01:31:34 +02:00
2022-01-28 11:44:36 +02:00
if gui.PrevLayout.Information != informationStr {
gui.c.SetViewContent(gui.Views.Information, informationStr)
2022-01-28 11:44:36 +02:00
gui.PrevLayout.Information = informationStr
2020-03-29 01:31:34 +02:00
}
2021-04-03 06:56:11 +02:00
if !gui.ViewsSetup {
if err := gui.onInitialViewsCreation(); err != nil {
return err
}
2021-04-03 06:56:11 +02:00
gui.ViewsSetup = true
}
2021-04-03 06:56:11 +02:00
if !gui.State.ViewsSetup {
if err := gui.onInitialViewsCreationForRepo(); err != nil {
2020-03-29 01:31:34 +02:00
return err
}
2021-04-03 06:56:11 +02:00
gui.State.ViewsSetup = true
2020-03-29 01:31:34 +02:00
}
2023-03-23 13:05:25 +02:00
for _, listContext := range gui.c.Context().AllList() {
view, err := gui.g.View(listContext.GetViewName())
2021-04-03 02:32:14 +02:00
if err != nil {
continue
}
2020-03-29 01:31:34 +02:00
2021-04-03 02:32:14 +02:00
view.SelBgColor = theme.GocuiSelectedLineBgColor
2020-08-16 01:18:57 +02:00
// I doubt this is expensive though it's admittedly redundant after the first render
view.SetOnSelectItem(gui.onSelectItemWrapper(listContext.OnSearchSelect))
2020-03-29 01:31:34 +02:00
}
2023-03-23 13:05:25 +02:00
for _, context := range gui.c.Context().AllPatchExplorer() {
context := context
context.GetView().SetOnSelectItem(gui.onSelectItemWrapper(
func(selectedLineIdx int) error {
context.GetMutex().Lock()
defer context.GetMutex().Unlock()
return context.NavigateTo(gui.c.IsCurrentContext(context), selectedLineIdx)
}),
)
}
2021-04-04 15:51:59 +02:00
mainViewWidth, mainViewHeight := gui.Views.Main.Size()
2022-01-28 11:44:36 +02:00
if mainViewWidth != gui.PrevLayout.MainWidth || mainViewHeight != gui.PrevLayout.MainHeight {
gui.PrevLayout.MainWidth = mainViewWidth
gui.PrevLayout.MainHeight = mainViewHeight
2020-03-29 01:31:34 +02:00
if err := gui.onResize(); err != nil {
return err
}
}
// here is a good place log some stuff
// if you run `lazygit --logs`
2020-03-29 01:31:34 +02:00
// this will let you see these branches as prettified json
2022-01-31 13:11:34 +02:00
// gui.c.Log.Info(utils.AsJson(gui.State.Model.Branches[0:4]))
2023-03-21 11:57:52 +02:00
return gui.helpers.Confirmation.ResizeCurrentPopupPanel()
2020-03-29 01:31:34 +02:00
}
2021-04-08 15:55:18 +02:00
func (gui *Gui) prepareView(viewName string) (*gocui.View, error) {
2021-04-04 15:51:59 +02:00
// arbitrarily giving the view enough size so that we don't get an error, but
// it's expected that the view will be given the correct size before being shown
return gui.g.SetView(viewName, 0, 0, 10, 10, 0)
}
2021-04-03 06:56:11 +02:00
func (gui *Gui) onInitialViewsCreationForRepo() error {
// hide any popup views. This only applies when we've just switched repos
for _, viewName := range gui.popupViewNames() {
2021-04-04 15:51:59 +02:00
view, err := gui.g.View(viewName)
if err == nil {
view.Visible = false
2021-04-03 06:56:11 +02:00
}
}
initialContext := gui.c.CurrentSideContext()
if err := gui.c.ActivateContext(initialContext); err != nil {
2021-04-03 06:56:11 +02:00
return err
}
return gui.loadNewRepo()
}
2023-03-23 13:41:24 +02:00
func (gui *Gui) popupViewNames() []string {
popups := slices.Filter(gui.State.Contexts.Flatten(), func(c types.Context) bool {
return c.GetKind() == types.PERSISTENT_POPUP || c.GetKind() == types.TEMPORARY_POPUP
})
return slices.Map(popups, func(c types.Context) string {
return c.GetViewName()
})
}
2021-04-03 06:56:11 +02:00
func (gui *Gui) onInitialViewsCreation() error {
2021-04-04 15:51:59 +02:00
// now we order the views (in order of bottom first)
2022-05-08 03:41:13 +02:00
for _, view := range gui.orderedViews() {
2021-04-04 15:51:59 +02:00
if _, err := gui.g.SetViewOnTop(view.Name()); err != nil {
return err
}
}
gui.g.Mutexes.ViewsMutex.Lock()
// add tabs to views
2020-09-30 00:27:12 +02:00
for _, view := range gui.g.Views() {
// if the view is in our mapping, we'll set the tabs and the tab index
for _, values := range gui.viewTabMap() {
index := slices.IndexFunc(values, func(tabContext context.TabView) bool {
return tabContext.ViewName == view.Name()
})
if index != -1 {
view.Tabs = slices.Map(values, func(tabContext context.TabView) string {
return tabContext.Tab
})
view.TabIndex = index
}
2020-09-30 00:27:12 +02:00
}
}
gui.g.Mutexes.ViewsMutex.Unlock()
2020-09-30 00:27:12 +02:00
if !gui.c.UserConfig.DisableStartupPopups {
2021-04-07 14:43:19 +02:00
popupTasks := []func(chan struct{}) error{}
storedPopupVersion := gui.c.GetAppState().StartupPopupVersion
2021-04-07 14:43:19 +02:00
if storedPopupVersion < StartupPopupVersion {
popupTasks = append(popupTasks, gui.showIntroPopupMessage)
}
gui.showInitialPopups(popupTasks)
}
if gui.showRecentRepos {
if err := gui.helpers.Repos.CreateRecentReposMenu(); err != nil {
return err
}
gui.showRecentRepos = false
}
gui.helpers.Update.CheckForUpdateInBackground()
2021-04-03 06:56:11 +02:00
gui.waitForIntro.Done()
return nil
2020-03-29 01:31:34 +02:00
}
// 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()
// for now we don't consider losing focus to a popup panel as actually losing focus
2023-03-21 11:57:52 +02:00
if newView != previousView && !gui.helpers.Confirmation.IsPopupPanel(newView.Name()) {
if err := gui.onViewFocusLost(previousView); err != nil {
return err
}
previousView = newView
}
return nil
}
}
func (gui *Gui) onViewFocusLost(oldView *gocui.View) error {
if oldView == nil {
return nil
}
oldView.Highlight = false
_ = oldView.SetOriginX(0)
return nil
}
2023-03-23 13:41:24 +02:00
func (gui *Gui) transientContexts() []types.Context {
return slices.Filter(gui.State.Contexts.Flatten(), func(context types.Context) bool {
return context.IsTransient()
})
}