1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-02-07 13:42:01 +02:00

add tooltip view for showing menu item descriptions

This commit is contained in:
Jesse Duffield 2022-05-08 12:46:48 +10:00
parent 517e9445df
commit f257740ea7
14 changed files with 85 additions and 45 deletions

View File

@ -91,8 +91,7 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
return gui.getConfirmationPanelDimensionsAux(panelWidth, panelHeight)
}
func (gui *Gui) getConfirmationPanelDimensionsForContentHeight(contentHeight int) (int, int, int, int) {
panelWidth := gui.getConfirmationPanelWidth()
func (gui *Gui) getConfirmationPanelDimensionsForContentHeight(panelWidth, contentHeight int) (int, int, int, int) {
return gui.getConfirmationPanelDimensionsAux(panelWidth, contentHeight)
}

View File

@ -19,15 +19,18 @@ var _ types.IListContext = (*MenuContext)(nil)
func NewMenuContext(
view *gocui.View,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.HelperCommon,
getOptionsMap func() map[string]string,
renderToDescriptionView func(string),
) *MenuContext {
viewModel := NewMenuViewModel()
onFocus := func(...types.OnFocusOpts) error {
selectedMenuItem := viewModel.GetSelected()
renderToDescriptionView(selectedMenuItem.Tooltip)
return nil
}
return &MenuContext{
MenuViewModel: viewModel,
ListContextTrait: &ListContextTrait{
@ -38,9 +41,7 @@ func NewMenuContext(
OnGetOptionsMap: getOptionsMap,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
OnFocus: onFocus,
}),
getDisplayStrings: viewModel.GetDisplayStrings,
list: viewModel,

View File

@ -55,11 +55,13 @@ func (self *UndoController) GetKeybindings(opts types.KeybindingsOpts) []*types.
Key: opts.GetKey(opts.Config.Universal.Undo),
Handler: self.reflogUndo,
Description: self.c.Tr.LcUndoReflog,
Tooltip: self.c.Tr.UndoTooltip,
},
{
Key: opts.GetKey(opts.Config.Universal.Redo),
Handler: self.reflogRedo,
Description: self.c.Tr.LcRedoReflog,
Tooltip: self.c.Tr.RedoTooltip,
},
}

View File

@ -31,7 +31,8 @@ func (self *FilesController) createResetMenu() error {
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
},
Key: 'D',
Key: 'D',
Tooltip: self.c.Tr.NukeDescription,
},
{
DisplayStrings: []string{

View File

@ -365,25 +365,6 @@ func (gui *Gui) syncViewContexts() {
}
}
func initialViewContextMapping(contextTree *context.ContextTree) map[string]types.Context {
return map[string]types.Context{
"status": contextTree.Status,
"files": contextTree.Files,
"branches": contextTree.Branches,
"remoteBranches": contextTree.RemoteBranches,
"commits": contextTree.LocalCommits,
"commitFiles": contextTree.CommitFiles,
"subCommits": contextTree.SubCommits,
"stash": contextTree.Stash,
"menu": contextTree.Menu,
"confirmation": contextTree.Confirmation,
"commitMessage": contextTree.CommitMessage,
"main": contextTree.Normal,
"secondary": contextTree.Normal,
"extras": contextTree.CommandLog,
}
}
// for now the split view will always be on
// NewGui builds a new gui handler
func NewGui(

View File

@ -88,6 +88,8 @@ func (gui *Gui) layout(g *gocui.Gui) error {
minimumWidth := 10
gui.Views.Limit.Visible = height < minimumHeight || width < minimumWidth
gui.Views.Tooltip.Visible = gui.Views.Menu.Visible && gui.Views.Tooltip.Buffer() != ""
for _, context := range gui.TransientContexts() {
view, err := gui.g.View(context.GetViewName())
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {

View File

@ -15,11 +15,11 @@ import (
func (gui *Gui) menuListContext() *context.MenuContext {
return context.NewMenuContext(
gui.Views.Menu,
nil,
nil,
nil,
gui.c,
gui.getMenuOptions,
func(content string) {
gui.Views.Tooltip.SetContent(content)
},
)
}

View File

@ -36,16 +36,18 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
}
}
x0, y0, x1, y1 := gui.getConfirmationPanelDimensionsForContentHeight(len(opts.Items))
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
menuView.Title = opts.Title
menuView.FgColor = theme.GocuiDefaultTextColor
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
gui.State.Contexts.Menu.SetMenuItems(opts.Items)
gui.State.Contexts.Menu.SetSelectedLineIdx(0)
gui.Views.Menu.Title = opts.Title
gui.Views.Menu.FgColor = theme.GocuiDefaultTextColor
gui.Views.Menu.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
return nil
}))
gui.State.Contexts.Menu.SetMenuItems(opts.Items)
gui.State.Contexts.Menu.SetSelectedLineIdx(0)
gui.Views.Tooltip.Wrap = true
gui.Views.Tooltip.FgColor = theme.GocuiDefaultTextColor
gui.Views.Tooltip.Visible = true
// resetting keybindings so that the menu-specific keybindings are registered
if err := gui.resetKeybindings(); err != nil {
@ -57,3 +59,16 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
// TODO: ensure that if we're opened a menu from within a menu that it renders correctly
return gui.c.PushContext(gui.State.Contexts.Menu)
}
func (gui *Gui) resizeMenu() {
itemCount := gui.State.Contexts.Menu.GetList().Len()
offset := 3
panelWidth := gui.getConfirmationPanelWidth()
x0, y0, x1, y1 := gui.getConfirmationPanelDimensionsForContentHeight(panelWidth, itemCount+offset)
menuBottom := y1 - offset
_, _ = gui.g.SetView("menu", x0, y0, x1, menuBottom, 0)
tooltipTop := menuBottom + 1
tooltipHeight := gui.getMessageHeight(true, gui.State.Contexts.Menu.GetSelected().Tooltip, panelWidth) + 2 // plus 2 for the frame
_, _ = gui.g.SetView("tooltip", x0, tooltipTop, x1, tooltipTop+tooltipHeight-1, 0)
}

View File

@ -73,7 +73,8 @@ func (gui *Gui) handleCreateOptionsMenu() error {
return binding.Handler()
},
Key: binding.Key,
Key: binding.Key,
Tooltip: binding.Tooltip,
}
})

View File

@ -118,6 +118,9 @@ type MenuItem struct {
// if Key is defined it allows the user to press the key to invoke the menu
// item, as opposed to having to navigate to it
Key Key
// the tooltip will be displayed upon highlighting the menu item
Tooltip string
}
type Model struct {

View File

@ -17,6 +17,9 @@ type Binding struct {
Alternative string
Tag string // e.g. 'navigation'. Used for grouping things in the cheatsheet
OpensMenu bool
// to be displayed if the keybinding is highlighted from within a menu
Tooltip string
}
// A guard is a decorator which checks something before executing a handler

View File

@ -47,9 +47,13 @@ func (gui *Gui) resizeCurrentPopupPanel() error {
if v == nil {
return nil
}
if gui.isPopupPanel(v.Name()) {
if v == gui.Views.Menu {
gui.resizeMenu()
} else if gui.isPopupPanel(v.Name()) {
return gui.resizePopupPanel(v, v.Buffer())
}
return nil
}

View File

@ -3,6 +3,8 @@ package gui
import (
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme"
)
@ -27,7 +29,7 @@ type Views struct {
SearchPrefix *gocui.View
Limit *gocui.View
Suggestions *gocui.View
Description *gocui.View
Tooltip *gocui.View
Extras *gocui.View
}
@ -71,7 +73,7 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
{viewPtr: &gui.Views.Menu, name: "menu"},
{viewPtr: &gui.Views.Suggestions, name: "suggestions"},
{viewPtr: &gui.Views.Confirmation, name: "confirmation"},
{viewPtr: &gui.Views.Description, name: "description"},
{viewPtr: &gui.Views.Tooltip, name: "tooltip"},
// this guy will cover everything else when it appears
{viewPtr: &gui.Views.Limit, name: "limit"},
@ -105,7 +107,6 @@ func (gui *Gui) controlledViews() []controlledView {
{viewName: "appStatus", windowName: "appStatus", frame: false},
{viewName: "information", windowName: "information", frame: false},
{viewName: "extras", windowName: "extras", frame: true},
{viewName: "description", windowName: "description", frame: true},
{viewName: "limit", windowName: "limit", frame: true},
}
}
@ -180,10 +181,12 @@ func (gui *Gui) createAllViews() error {
gui.Views.Suggestions.Visible = false
gui.Views.Description.FgColor = theme.GocuiDefaultTextColor
gui.Views.Tooltip.FgColor = theme.GocuiDefaultTextColor
gui.Views.Menu.Visible = false
gui.Views.Tooltip.Visible = false
gui.Views.Information.BgColor = gocui.ColorDefault
gui.Views.Information.FgColor = gocui.ColorGreen
@ -194,3 +197,22 @@ func (gui *Gui) createAllViews() error {
return nil
}
func initialViewContextMapping(contextTree *context.ContextTree) map[string]types.Context {
return map[string]types.Context{
"status": contextTree.Status,
"files": contextTree.Files,
"branches": contextTree.Branches,
"remoteBranches": contextTree.RemoteBranches,
"commits": contextTree.LocalCommits,
"commitFiles": contextTree.CommitFiles,
"subCommits": contextTree.SubCommits,
"stash": contextTree.Stash,
"menu": contextTree.Menu,
"confirmation": contextTree.Confirmation,
"commitMessage": contextTree.CommitMessage,
"main": contextTree.Normal,
"secondary": contextTree.Normal,
"extras": contextTree.CommandLog,
}
}

View File

@ -108,6 +108,8 @@ type TranslationSet struct {
LcUndo string
LcUndoReflog string
LcRedoReflog string
UndoTooltip string
RedoTooltip string
LcPop string
LcDrop string
LcApply string
@ -486,6 +488,7 @@ type TranslationSet struct {
CheckoutPrompt string
HardResetAutostashPrompt string
UpstreamGone string
NukeDescription string
Actions Actions
Bisect Bisect
}
@ -719,6 +722,8 @@ func EnglishTranslationSet() TranslationSet {
LcUndo: "undo",
LcUndoReflog: "undo (via reflog) (experimental)",
LcRedoReflog: "redo (via reflog) (experimental)",
UndoTooltip: "The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration.",
RedoTooltip: "The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration.",
LcPop: "pop",
LcDrop: "drop",
LcApply: "apply",
@ -1098,6 +1103,7 @@ func EnglishTranslationSet() TranslationSet {
HardResetAutostashPrompt: "Are you sure you want to hard reset to '%s'? An auto-stash will be performed if necessary.",
CheckoutPrompt: "Are you sure you want to checkout '%s'?",
UpstreamGone: "(upstream gone)",
NukeDescription: "If you want to make all the changes in the worktree go away, this is the way to do it. If there are dirty submodule changes this will stash those changes in the submodule(s).",
Actions: Actions{
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
CheckoutCommit: "Checkout commit",