Stagger popup panels (#3694)

- **PR Description**

Sometimes we open a popup panel on top of another one; an example is the
`<c-o>` menu in the commit message editor, or the "add co-author" prompt
that appears on top of the commit message panel. We just added a new one
in #3676, which is the confirmation that appears when pasting a commit
message over an existing one.

Currently, these panels are sized and positioned independently of each
other, which looks ugly and makes it hard to see what's going on:
<img width="720" alt="image"
src="6d84fc85-fadb-4f03-a973-868bc29d79f5">

Improve this by offsetting the new panel a bit down and to the right
from the one below it:
<img width="750" alt="image"
src="c0c39f88-356b-4add-b335-4db2e54496ed">

Along the way we clean up the code a bit and fix a few minor issues; see
the individual commit messages for details.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
This commit is contained in:
Stefan Haller 2024-06-28 08:17:36 +02:00 committed by GitHub
commit 26c3e0d333
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 76 additions and 97 deletions

2
go.mod
View file

@ -16,7 +16,7 @@ require (
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20240623124136-ce5274be521d
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e

4
go.sum
View file

@ -188,8 +188,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20240623124136-ce5274be521d h1:I6rViLB+ZW5SnS8P7ZE0FdY6lMfx803qZ9ZYEYCvfro=
github.com/jesseduffield/gocui v0.3.1-0.20240623124136-ce5274be521d/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b h1:oxCq0DvR2GMGf4UaoaASb0nQK/fJMQW3c3PNCLWCjS8=
github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=

View file

@ -391,3 +391,12 @@ func (self *ContextMgr) ContextForKey(key types.ContextKey) types.Context {
return nil
}
func (self *ContextMgr) PopupContexts() []types.Context {
self.RLock()
defer self.RUnlock()
return lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
return context.GetKind() == types.TEMPORARY_POPUP || context.GetKind() == types.PERSISTENT_POPUP
})
}

View file

@ -116,6 +116,8 @@ func (self *CommitMessageContext) SetPanelState(
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.CommitMenu),
})
self.c.Views().CommitDescription.Visible = true
}
func (self *CommitMessageContext) RenderCommitLength() {

View file

@ -404,7 +404,6 @@ func (gui *Gui) getCommitMessageSetTextareaTextFn(getView func() *gocui.View) fu
view := getView()
view.ClearTextArea()
view.TextArea.TypeString(text)
gui.helpers.Confirmation.ResizeCommitMessagePanels()
view.RenderTextArea()
}
}

View file

@ -53,7 +53,7 @@ func (self *CommitDescriptionController) context() *context.CommitMessageContext
}
func (self *CommitDescriptionController) switchToCommitMessage() error {
return self.c.PushContext(self.c.Contexts().CommitMessage)
return self.c.ReplaceContext(self.c.Contexts().CommitMessage)
}
func (self *CommitDescriptionController) close() error {

View file

@ -85,7 +85,7 @@ func (self *CommitMessageController) handleNextCommit() error {
}
func (self *CommitMessageController) switchToCommitDescription() error {
if err := self.c.PushContext(self.c.Contexts().CommitDescription); err != nil {
if err := self.c.ReplaceContext(self.c.Contexts().CommitDescription); err != nil {
return err
}
return nil

View file

@ -214,7 +214,7 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
PreserveMessage: false,
OnConfirm: func(summary string, description string) error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
_ = self.c.Helpers().Commits.PopCommitMessageContexts()
_ = self.c.Helpers().Commits.CloseCommitMessagePanel()
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoNewCommit)
err := self.c.Git().Patch.PullPatchIntoNewCommit(self.c.Model().Commits, commitIndex, summary, description)
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {

View file

@ -154,7 +154,7 @@ func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOp
self.UpdateCommitPanelView(opts.InitialMessage)
return self.pushCommitMessageContexts()
return self.c.PushContext(self.c.Contexts().CommitMessage)
}
func (self *CommitsHelper) OnCommitSuccess() {
@ -190,28 +190,10 @@ func (self *CommitsHelper) CloseCommitMessagePanel() error {
self.c.Contexts().CommitMessage.SetHistoryMessage("")
return self.PopCommitMessageContexts()
}
self.c.Views().CommitMessage.Visible = false
self.c.Views().CommitDescription.Visible = false
func (self *CommitsHelper) PopCommitMessageContexts() error {
return self.c.RemoveContexts(self.commitMessageContexts())
}
func (self *CommitsHelper) pushCommitMessageContexts() error {
for _, context := range self.commitMessageContexts() {
if err := self.c.PushContext(context); err != nil {
return err
}
}
return nil
}
func (self *CommitsHelper) commitMessageContexts() []types.Context {
return []types.Context{
self.c.Contexts().CommitDescription,
self.c.Contexts().CommitMessage,
}
return self.c.PopContext()
}
func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.Suggestion) error {

View file

@ -5,8 +5,6 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme"
@ -118,21 +116,23 @@ func wrapMessageToWidth(wrap bool, message string, width int) []string {
return wrappedLines
}
func (self *ConfirmationHelper) getPopupPanelDimensions(wrap bool, prompt string) (int, int, int, int) {
panelWidth := self.getPopupPanelWidth()
panelHeight := getMessageHeight(wrap, prompt, panelWidth)
return self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int, parentPopupContext types.Context) (int, int, int, int) {
return self.getPopupPanelDimensionsAux(panelWidth, contentHeight, parentPopupContext)
}
func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int) (int, int, int, int) {
return self.getPopupPanelDimensionsAux(panelWidth, contentHeight)
}
func (self *ConfirmationHelper) getPopupPanelDimensionsAux(panelWidth int, panelHeight int) (int, int, int, int) {
func (self *ConfirmationHelper) getPopupPanelDimensionsAux(panelWidth int, panelHeight int, parentPopupContext types.Context) (int, int, int, int) {
width, height := self.c.GocuiGui().Size()
if panelHeight > height*3/4 {
panelHeight = height * 3 / 4
}
if parentPopupContext != nil {
// If there's already a popup on the screen, offset the new one from its
// parent so that it's clearly distinguished from the parent
x0, y0, _, _ := parentPopupContext.GetView().Dimensions()
x0 += 2
y0 += 1
return x0, y0, x0 + panelWidth, y0 + panelHeight + 1
}
return width/2 - panelWidth/2,
height/2 - panelHeight/2 - panelHeight%2 - 1,
width/2 + panelWidth/2,
@ -177,7 +177,6 @@ func (self *ConfirmationHelper) prepareConfirmationPanel(
suggestionsView.Subtitle = ""
}
self.ResizeConfirmationPanel()
return nil
}
@ -227,7 +226,6 @@ func (self *ConfirmationHelper) CreatePopupPanel(ctx goContext.Context, opts typ
textArea := confirmationView.TextArea
textArea.Clear()
textArea.TypeString(opts.Prompt)
self.ResizeConfirmationPanel()
confirmationView.RenderTextArea()
} else {
self.c.ResetViewOrigin(confirmationView)
@ -325,49 +323,23 @@ func (self *ConfirmationHelper) getSelectedSuggestionValue() string {
return ""
}
func (self *ConfirmationHelper) ResizeConfirmationPanel() {
suggestionsViewHeight := 0
if self.c.Views().Suggestions.Visible {
suggestionsViewHeight = 11
}
panelWidth := self.getPopupPanelWidth()
prompt := self.c.Views().Confirmation.Buffer()
wrap := true
if self.c.Views().Confirmation.Editable {
prompt = self.c.Views().Confirmation.TextArea.GetContent()
wrap = false
}
panelHeight := getMessageHeight(wrap, prompt, panelWidth) + suggestionsViewHeight
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
confirmationViewBottom := y1 - suggestionsViewHeight
_, _ = self.c.GocuiGui().SetView(self.c.Views().Confirmation.Name(), x0, y0, x1, confirmationViewBottom, 0)
func (self *ConfirmationHelper) ResizeCurrentPopupPanels() {
var parentPopupContext types.Context
for _, c := range self.c.CurrentPopupContexts() {
switch c {
case self.c.Contexts().Menu:
self.resizeMenu(parentPopupContext)
case self.c.Contexts().Confirmation, self.c.Contexts().Suggestions:
self.resizeConfirmationPanel(parentPopupContext)
case self.c.Contexts().CommitMessage, self.c.Contexts().CommitDescription:
self.ResizeCommitMessagePanels(parentPopupContext)
}
suggestionsViewTop := confirmationViewBottom + 1
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
parentPopupContext = c
}
}
func (self *ConfirmationHelper) ResizeCurrentPopupPanel() error {
c := self.c.CurrentContext()
switch c {
case self.c.Contexts().Menu:
self.resizeMenu()
case self.c.Contexts().Confirmation, self.c.Contexts().Suggestions:
self.resizeConfirmationPanel()
case self.c.Contexts().CommitMessage, self.c.Contexts().CommitDescription:
self.ResizeCommitMessagePanels()
}
return nil
}
func (self *ConfirmationHelper) ResizePopupPanel(v *gocui.View, content string) error {
x0, y0, x1, y1 := self.getPopupPanelDimensions(v.Wrap, content)
_, err := self.c.GocuiGui().SetView(v.Name(), x0, y0, x1, y1, 0)
return err
}
func (self *ConfirmationHelper) resizeMenu() {
func (self *ConfirmationHelper) resizeMenu(parentPopupContext types.Context) {
// we want the unfiltered length here so that if we're filtering we don't
// resize the window
itemCount := self.c.Contexts().Menu.UnfilteredLen()
@ -375,7 +347,7 @@ func (self *ConfirmationHelper) resizeMenu() {
panelWidth := self.getPopupPanelWidth()
contentWidth := panelWidth - 2 // minus 2 for the frame
promptLinesCount := self.layoutMenuPrompt(contentWidth)
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset+promptLinesCount)
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset+promptLinesCount, parentPopupContext)
menuBottom := y1 - offset
_, _ = self.c.GocuiGui().SetView(self.c.Views().Menu.Name(), x0, y0, x1, menuBottom, 0)
@ -418,7 +390,7 @@ func (self *ConfirmationHelper) layoutMenuPrompt(contentWidth int) int {
return len(promptLines)
}
func (self *ConfirmationHelper) resizeConfirmationPanel() {
func (self *ConfirmationHelper) resizeConfirmationPanel(parentPopupContext types.Context) {
suggestionsViewHeight := 0
if self.c.Views().Suggestions.Visible {
suggestionsViewHeight = 11
@ -431,7 +403,7 @@ func (self *ConfirmationHelper) resizeConfirmationPanel() {
wrap = false
}
panelHeight := getMessageHeight(wrap, prompt, panelWidth) + suggestionsViewHeight
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
confirmationViewBottom := y1 - suggestionsViewHeight
_, _ = self.c.GocuiGui().SetView(self.c.Views().Confirmation.Name(), x0, y0, x1, confirmationViewBottom, 0)
@ -439,7 +411,7 @@ func (self *ConfirmationHelper) resizeConfirmationPanel() {
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
}
func (self *ConfirmationHelper) ResizeCommitMessagePanels() {
func (self *ConfirmationHelper) ResizeCommitMessagePanels(parentPopupContext types.Context) {
panelWidth := self.getPopupPanelWidth()
content := self.c.Views().CommitDescription.TextArea.GetContent()
summaryViewHeight := 3
@ -448,18 +420,18 @@ func (self *ConfirmationHelper) ResizeCommitMessagePanels() {
if panelHeight < minHeight {
panelHeight = minHeight
}
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight)
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
_, _ = self.c.GocuiGui().SetView(self.c.Views().CommitMessage.Name(), x0, y0, x1, y0+summaryViewHeight-1, 0)
_, _ = self.c.GocuiGui().SetView(self.c.Views().CommitDescription.Name(), x0, y0+summaryViewHeight, x1, y1+summaryViewHeight, 0)
}
func (self *ConfirmationHelper) IsPopupPanel(viewName string) bool {
return viewName == "commitMessage" || viewName == "confirmation" || viewName == "menu"
func (self *ConfirmationHelper) IsPopupPanel(context types.Context) bool {
return context.GetKind() == types.PERSISTENT_POPUP || context.GetKind() == types.TEMPORARY_POPUP
}
func (self *ConfirmationHelper) IsPopupPanelFocused() bool {
return self.IsPopupPanel(self.c.CurrentContext().GetViewName())
return self.IsPopupPanel(self.c.CurrentContext())
}
func (self *ConfirmationHelper) TooltipForMenuItem(menuItem *types.MenuItem) string {

View file

@ -73,6 +73,10 @@ func (self *guiCommon) CurrentSideContext() types.Context {
return self.gui.State.ContextMgr.CurrentSide()
}
func (self *guiCommon) CurrentPopupContexts() []types.Context {
return self.gui.State.ContextMgr.PopupContexts()
}
func (self *guiCommon) IsCurrentContext(c types.Context) bool {
return self.gui.State.ContextMgr.IsCurrent(c)
}

View file

@ -173,9 +173,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
// if you run `lazygit --logs`
// this will let you see these branches as prettified json
// gui.c.Log.Info(utils.AsJson(gui.State.Model.Branches[0:4]))
if err := gui.helpers.Confirmation.ResizeCurrentPopupPanel(); err != nil {
return err
}
gui.helpers.Confirmation.ResizeCurrentPopupPanels()
gui.renderContextOptionsMap()

View file

@ -67,6 +67,7 @@ type IGuiCommon interface {
CurrentContext() Context
CurrentStaticContext() Context
CurrentSideContext() Context
CurrentPopupContexts() []Context
IsCurrentContext(Context) bool
// TODO: replace the above context-based methods with just using Context() e.g. replace PushContext() with Context().Push()
Context() IContextMgr

View file

@ -307,14 +307,26 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, er
}
if v, err := g.View(name); err == nil {
if v.x0 != x0 || v.x1 != x1 || v.y0 != y0 || v.y1 != y1 {
v.clearViewLines()
}
sizeChanged := v.x0 != x0 || v.x1 != x1 || v.y0 != y0 || v.y1 != y1
v.x0 = x0
v.y0 = y0
v.x1 = x1
v.y1 = y1
if sizeChanged {
v.clearViewLines()
if v.Editable {
cursorX, cursorY := v.TextArea.GetCursorXY()
newViewCursorX, newOriginX := updatedCursorAndOrigin(0, v.InnerWidth(), cursorX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(0, v.InnerHeight(), cursorY)
_ = v.SetCursor(newViewCursorX, newViewCursorY)
_ = v.SetOrigin(newOriginX, newOriginY)
}
}
return v, nil
}

2
vendor/modules.txt vendored
View file

@ -172,7 +172,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
# github.com/jesseduffield/gocui v0.3.1-0.20240623124136-ce5274be521d
# github.com/jesseduffield/gocui v0.3.1-0.20240628061234-aed9e133e65b
## explicit; go 1.12
github.com/jesseduffield/gocui
# github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10