1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-15 00:15:32 +02:00

Rerender certain views when their width changes

Situations where a view's width changes:
- changing screen modes
- enter staging or patch building
- resizing the terminal window

For the first of these we currently have special code to force a rerender, since
some views render different content depending on whether they are in full-screen
mode. We'll be able to remove that code now, since this new generic mechanism
takes care of that too.

But we will need this more general mechanism for cases where views truncate
their content to the view width; we'll add one example for that later in this
branch.
This commit is contained in:
Stefan Haller
2023-10-14 12:45:13 +02:00
parent 3cee4de39c
commit d5b4f7bb3e
8 changed files with 88 additions and 52 deletions

View File

@ -20,10 +20,11 @@ type BaseContext struct {
onFocusFn onFocusFn onFocusFn onFocusFn
onFocusLostFn onFocusLostFn onFocusLostFn onFocusLostFn
focusable bool focusable bool
transient bool transient bool
hasControlledBounds bool hasControlledBounds bool
highlightOnFocus bool needsRerenderOnWidthChange bool
highlightOnFocus bool
*ParentContextMgr *ParentContextMgr
} }
@ -36,14 +37,15 @@ type (
var _ types.IBaseContext = &BaseContext{} var _ types.IBaseContext = &BaseContext{}
type NewBaseContextOpts struct { type NewBaseContextOpts struct {
Kind types.ContextKind Kind types.ContextKind
Key types.ContextKey Key types.ContextKey
View *gocui.View View *gocui.View
WindowName string WindowName string
Focusable bool Focusable bool
Transient bool Transient bool
HasUncontrolledBounds bool // negating for the sake of making false the default HasUncontrolledBounds bool // negating for the sake of making false the default
HighlightOnFocus bool HighlightOnFocus bool
NeedsRerenderOnWidthChange bool
OnGetOptionsMap func() map[string]string OnGetOptionsMap func() map[string]string
} }
@ -54,17 +56,18 @@ func NewBaseContext(opts NewBaseContextOpts) *BaseContext {
hasControlledBounds := !opts.HasUncontrolledBounds hasControlledBounds := !opts.HasUncontrolledBounds
return &BaseContext{ return &BaseContext{
kind: opts.Kind, kind: opts.Kind,
key: opts.Key, key: opts.Key,
view: opts.View, view: opts.View,
windowName: opts.WindowName, windowName: opts.WindowName,
onGetOptionsMap: opts.OnGetOptionsMap, onGetOptionsMap: opts.OnGetOptionsMap,
focusable: opts.Focusable, focusable: opts.Focusable,
transient: opts.Transient, transient: opts.Transient,
hasControlledBounds: hasControlledBounds, hasControlledBounds: hasControlledBounds,
highlightOnFocus: opts.HighlightOnFocus, highlightOnFocus: opts.HighlightOnFocus,
ParentContextMgr: &ParentContextMgr{}, needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
viewTrait: viewTrait, ParentContextMgr: &ParentContextMgr{},
viewTrait: viewTrait,
} }
} }
@ -190,6 +193,10 @@ func (self *BaseContext) HasControlledBounds() bool {
return self.hasControlledBounds return self.hasControlledBounds
} }
func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
return self.needsRerenderOnWidthChange
}
func (self *BaseContext) Title() string { func (self *BaseContext) Title() string {
return "" return ""
} }

View File

@ -40,11 +40,12 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
FilteredListViewModel: viewModel, FilteredListViewModel: viewModel,
ListContextTrait: &ListContextTrait{ ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Branches, View: c.Views().Branches,
WindowName: "branches", WindowName: "branches",
Key: LOCAL_BRANCHES_CONTEXT_KEY, Key: LOCAL_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
NeedsRerenderOnWidthChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -68,11 +68,12 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
SearchTrait: NewSearchTrait(c), SearchTrait: NewSearchTrait(c),
ListContextTrait: &ListContextTrait{ ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Commits, View: c.Views().Commits,
WindowName: "commits", WindowName: "commits",
Key: LOCAL_COMMITS_CONTEXT_KEY, Key: LOCAL_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
NeedsRerenderOnWidthChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -43,11 +43,12 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
FilteredListViewModel: viewModel, FilteredListViewModel: viewModel,
ListContextTrait: &ListContextTrait{ ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().ReflogCommits, View: c.Views().ReflogCommits,
WindowName: "commits", WindowName: "commits",
Key: REFLOG_COMMITS_CONTEXT_KEY, Key: REFLOG_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
NeedsRerenderOnWidthChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -36,12 +36,13 @@ func NewRemoteBranchesContext(
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle), DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
ListContextTrait: &ListContextTrait{ ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().RemoteBranches, View: c.Views().RemoteBranches,
WindowName: "branches", WindowName: "branches",
Key: REMOTE_BRANCHES_CONTEXT_KEY, Key: REMOTE_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
Transient: true, Transient: true,
NeedsRerenderOnWidthChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -115,12 +115,13 @@ func NewSubCommitsContext(
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle), DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle),
ListContextTrait: &ListContextTrait{ ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().SubCommits, View: c.Views().SubCommits,
WindowName: "branches", WindowName: "branches",
Key: SUB_COMMITS_CONTEXT_KEY, Key: SUB_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
Transient: true, Transient: true,
NeedsRerenderOnWidthChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -44,8 +44,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
} }
} }
contextsToRerender := []types.Context{}
// we assume that the view has already been created. // we assume that the view has already been created.
setViewFromDimensions := func(viewName string, windowName string) (*gocui.View, error) { setViewFromDimensions := func(context types.Context) (*gocui.View, error) {
viewName := context.GetViewName()
windowName := context.GetWindowName()
dimensionsObj, ok := viewDimensions[windowName] dimensionsObj, ok := viewDimensions[windowName]
view, err := g.View(viewName) view, err := g.View(viewName)
@ -67,6 +72,16 @@ func (gui *Gui) layout(g *gocui.Gui) error {
if view.Frame { if view.Frame {
frameOffset = 0 frameOffset = 0
} }
if context.NeedsRerenderOnWidthChange() {
// view.Width() returns the width -1 for some reason
oldWidth := view.Width() + 1
newWidth := dimensionsObj.X1 - dimensionsObj.X0 + 2*frameOffset
if oldWidth != newWidth {
contextsToRerender = append(contextsToRerender, context)
}
}
_, err = g.SetView( _, err = g.SetView(
viewName, viewName,
dimensionsObj.X0-frameOffset, dimensionsObj.X0-frameOffset,
@ -85,7 +100,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
continue continue
} }
_, err := setViewFromDimensions(context.GetViewName(), context.GetWindowName()) _, err := setViewFromDimensions(context)
if err != nil && !gocui.IsUnknownView(err) { if err != nil && !gocui.IsUnknownView(err) {
return err return err
} }
@ -146,6 +161,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
} }
} }
for _, context := range contextsToRerender {
if err := context.HandleRender(); err != nil {
return err
}
}
// here is a good place log some stuff // here is a good place log some stuff
// if you run `lazygit --logs` // if you run `lazygit --logs`
// this will let you see these branches as prettified json // this will let you see these branches as prettified json

View File

@ -60,6 +60,9 @@ type IBaseContext interface {
// determined independently. // determined independently.
HasControlledBounds() bool HasControlledBounds() bool
// true if the view needs to be rerendered when its width changes
NeedsRerenderOnWidthChange() bool
// returns the desired title for the view upon activation. If there is no desired title (returns empty string), then // returns the desired title for the view upon activation. If there is no desired title (returns empty string), then
// no title will be set // no title will be set
Title() string Title() string