mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-13 22:17:05 +02:00
add tooltip view for showing menu item descriptions
This commit is contained in:
parent
517e9445df
commit
f257740ea7
@ -91,8 +91,7 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
|
|||||||
return gui.getConfirmationPanelDimensionsAux(panelWidth, panelHeight)
|
return gui.getConfirmationPanelDimensionsAux(panelWidth, panelHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getConfirmationPanelDimensionsForContentHeight(contentHeight int) (int, int, int, int) {
|
func (gui *Gui) getConfirmationPanelDimensionsForContentHeight(panelWidth, contentHeight int) (int, int, int, int) {
|
||||||
panelWidth := gui.getConfirmationPanelWidth()
|
|
||||||
return gui.getConfirmationPanelDimensionsAux(panelWidth, contentHeight)
|
return gui.getConfirmationPanelDimensionsAux(panelWidth, contentHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,15 +19,18 @@ var _ types.IListContext = (*MenuContext)(nil)
|
|||||||
func NewMenuContext(
|
func NewMenuContext(
|
||||||
view *gocui.View,
|
view *gocui.View,
|
||||||
|
|
||||||
onFocus func(...types.OnFocusOpts) error,
|
|
||||||
onRenderToMain func(...types.OnFocusOpts) error,
|
|
||||||
onFocusLost func() error,
|
|
||||||
|
|
||||||
c *types.HelperCommon,
|
c *types.HelperCommon,
|
||||||
getOptionsMap func() map[string]string,
|
getOptionsMap func() map[string]string,
|
||||||
|
renderToDescriptionView func(string),
|
||||||
) *MenuContext {
|
) *MenuContext {
|
||||||
viewModel := NewMenuViewModel()
|
viewModel := NewMenuViewModel()
|
||||||
|
|
||||||
|
onFocus := func(...types.OnFocusOpts) error {
|
||||||
|
selectedMenuItem := viewModel.GetSelected()
|
||||||
|
renderToDescriptionView(selectedMenuItem.Tooltip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return &MenuContext{
|
return &MenuContext{
|
||||||
MenuViewModel: viewModel,
|
MenuViewModel: viewModel,
|
||||||
ListContextTrait: &ListContextTrait{
|
ListContextTrait: &ListContextTrait{
|
||||||
@ -38,9 +41,7 @@ func NewMenuContext(
|
|||||||
OnGetOptionsMap: getOptionsMap,
|
OnGetOptionsMap: getOptionsMap,
|
||||||
Focusable: true,
|
Focusable: true,
|
||||||
}), ContextCallbackOpts{
|
}), ContextCallbackOpts{
|
||||||
OnFocus: onFocus,
|
OnFocus: onFocus,
|
||||||
OnFocusLost: onFocusLost,
|
|
||||||
OnRenderToMain: onRenderToMain,
|
|
||||||
}),
|
}),
|
||||||
getDisplayStrings: viewModel.GetDisplayStrings,
|
getDisplayStrings: viewModel.GetDisplayStrings,
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
|
@ -55,11 +55,13 @@ func (self *UndoController) GetKeybindings(opts types.KeybindingsOpts) []*types.
|
|||||||
Key: opts.GetKey(opts.Config.Universal.Undo),
|
Key: opts.GetKey(opts.Config.Universal.Undo),
|
||||||
Handler: self.reflogUndo,
|
Handler: self.reflogUndo,
|
||||||
Description: self.c.Tr.LcUndoReflog,
|
Description: self.c.Tr.LcUndoReflog,
|
||||||
|
Tooltip: self.c.Tr.UndoTooltip,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Redo),
|
Key: opts.GetKey(opts.Config.Universal.Redo),
|
||||||
Handler: self.reflogRedo,
|
Handler: self.reflogRedo,
|
||||||
Description: self.c.Tr.LcRedoReflog,
|
Description: self.c.Tr.LcRedoReflog,
|
||||||
|
Tooltip: self.c.Tr.RedoTooltip,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ func (self *FilesController) createResetMenu() error {
|
|||||||
|
|
||||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
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{
|
DisplayStrings: []string{
|
||||||
|
@ -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
|
// for now the split view will always be on
|
||||||
// NewGui builds a new gui handler
|
// NewGui builds a new gui handler
|
||||||
func NewGui(
|
func NewGui(
|
||||||
|
@ -88,6 +88,8 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
minimumWidth := 10
|
minimumWidth := 10
|
||||||
gui.Views.Limit.Visible = height < minimumHeight || width < minimumWidth
|
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() {
|
for _, context := range gui.TransientContexts() {
|
||||||
view, err := gui.g.View(context.GetViewName())
|
view, err := gui.g.View(context.GetViewName())
|
||||||
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
|
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
|
||||||
|
@ -15,11 +15,11 @@ import (
|
|||||||
func (gui *Gui) menuListContext() *context.MenuContext {
|
func (gui *Gui) menuListContext() *context.MenuContext {
|
||||||
return context.NewMenuContext(
|
return context.NewMenuContext(
|
||||||
gui.Views.Menu,
|
gui.Views.Menu,
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
gui.c,
|
gui.c,
|
||||||
gui.getMenuOptions,
|
gui.getMenuOptions,
|
||||||
|
func(content string) {
|
||||||
|
gui.Views.Tooltip.SetContent(content)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,16 +36,18 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensionsForContentHeight(len(opts.Items))
|
gui.State.Contexts.Menu.SetMenuItems(opts.Items)
|
||||||
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
gui.State.Contexts.Menu.SetSelectedLineIdx(0)
|
||||||
menuView.Title = opts.Title
|
|
||||||
menuView.FgColor = theme.GocuiDefaultTextColor
|
gui.Views.Menu.Title = opts.Title
|
||||||
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
gui.Views.Menu.FgColor = theme.GocuiDefaultTextColor
|
||||||
|
gui.Views.Menu.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
gui.State.Contexts.Menu.SetMenuItems(opts.Items)
|
gui.Views.Tooltip.Wrap = true
|
||||||
gui.State.Contexts.Menu.SetSelectedLineIdx(0)
|
gui.Views.Tooltip.FgColor = theme.GocuiDefaultTextColor
|
||||||
|
gui.Views.Tooltip.Visible = true
|
||||||
|
|
||||||
// resetting keybindings so that the menu-specific keybindings are registered
|
// resetting keybindings so that the menu-specific keybindings are registered
|
||||||
if err := gui.resetKeybindings(); err != nil {
|
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
|
// 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)
|
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)
|
||||||
|
}
|
||||||
|
@ -73,7 +73,8 @@ func (gui *Gui) handleCreateOptionsMenu() error {
|
|||||||
|
|
||||||
return binding.Handler()
|
return binding.Handler()
|
||||||
},
|
},
|
||||||
Key: binding.Key,
|
Key: binding.Key,
|
||||||
|
Tooltip: binding.Tooltip,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -118,6 +118,9 @@ type MenuItem struct {
|
|||||||
// if Key is defined it allows the user to press the key to invoke the menu
|
// 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
|
// item, as opposed to having to navigate to it
|
||||||
Key Key
|
Key Key
|
||||||
|
|
||||||
|
// the tooltip will be displayed upon highlighting the menu item
|
||||||
|
Tooltip string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
|
@ -17,6 +17,9 @@ type Binding struct {
|
|||||||
Alternative string
|
Alternative string
|
||||||
Tag string // e.g. 'navigation'. Used for grouping things in the cheatsheet
|
Tag string // e.g. 'navigation'. Used for grouping things in the cheatsheet
|
||||||
OpensMenu bool
|
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
|
// A guard is a decorator which checks something before executing a handler
|
||||||
|
@ -47,9 +47,13 @@ func (gui *Gui) resizeCurrentPopupPanel() error {
|
|||||||
if v == nil {
|
if v == nil {
|
||||||
return 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 gui.resizePopupPanel(v, v.Buffer())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ type Views struct {
|
|||||||
SearchPrefix *gocui.View
|
SearchPrefix *gocui.View
|
||||||
Limit *gocui.View
|
Limit *gocui.View
|
||||||
Suggestions *gocui.View
|
Suggestions *gocui.View
|
||||||
Description *gocui.View
|
Tooltip *gocui.View
|
||||||
Extras *gocui.View
|
Extras *gocui.View
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
|||||||
{viewPtr: &gui.Views.Menu, name: "menu"},
|
{viewPtr: &gui.Views.Menu, name: "menu"},
|
||||||
{viewPtr: &gui.Views.Suggestions, name: "suggestions"},
|
{viewPtr: &gui.Views.Suggestions, name: "suggestions"},
|
||||||
{viewPtr: &gui.Views.Confirmation, name: "confirmation"},
|
{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
|
// this guy will cover everything else when it appears
|
||||||
{viewPtr: &gui.Views.Limit, name: "limit"},
|
{viewPtr: &gui.Views.Limit, name: "limit"},
|
||||||
@ -105,7 +107,6 @@ func (gui *Gui) controlledViews() []controlledView {
|
|||||||
{viewName: "appStatus", windowName: "appStatus", frame: false},
|
{viewName: "appStatus", windowName: "appStatus", frame: false},
|
||||||
{viewName: "information", windowName: "information", frame: false},
|
{viewName: "information", windowName: "information", frame: false},
|
||||||
{viewName: "extras", windowName: "extras", frame: true},
|
{viewName: "extras", windowName: "extras", frame: true},
|
||||||
{viewName: "description", windowName: "description", frame: true},
|
|
||||||
{viewName: "limit", windowName: "limit", frame: true},
|
{viewName: "limit", windowName: "limit", frame: true},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,10 +181,12 @@ func (gui *Gui) createAllViews() error {
|
|||||||
|
|
||||||
gui.Views.Suggestions.Visible = false
|
gui.Views.Suggestions.Visible = false
|
||||||
|
|
||||||
gui.Views.Description.FgColor = theme.GocuiDefaultTextColor
|
gui.Views.Tooltip.FgColor = theme.GocuiDefaultTextColor
|
||||||
|
|
||||||
gui.Views.Menu.Visible = false
|
gui.Views.Menu.Visible = false
|
||||||
|
|
||||||
|
gui.Views.Tooltip.Visible = false
|
||||||
|
|
||||||
gui.Views.Information.BgColor = gocui.ColorDefault
|
gui.Views.Information.BgColor = gocui.ColorDefault
|
||||||
gui.Views.Information.FgColor = gocui.ColorGreen
|
gui.Views.Information.FgColor = gocui.ColorGreen
|
||||||
|
|
||||||
@ -194,3 +197,22 @@ func (gui *Gui) createAllViews() error {
|
|||||||
|
|
||||||
return nil
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -108,6 +108,8 @@ type TranslationSet struct {
|
|||||||
LcUndo string
|
LcUndo string
|
||||||
LcUndoReflog string
|
LcUndoReflog string
|
||||||
LcRedoReflog string
|
LcRedoReflog string
|
||||||
|
UndoTooltip string
|
||||||
|
RedoTooltip string
|
||||||
LcPop string
|
LcPop string
|
||||||
LcDrop string
|
LcDrop string
|
||||||
LcApply string
|
LcApply string
|
||||||
@ -486,6 +488,7 @@ type TranslationSet struct {
|
|||||||
CheckoutPrompt string
|
CheckoutPrompt string
|
||||||
HardResetAutostashPrompt string
|
HardResetAutostashPrompt string
|
||||||
UpstreamGone string
|
UpstreamGone string
|
||||||
|
NukeDescription string
|
||||||
Actions Actions
|
Actions Actions
|
||||||
Bisect Bisect
|
Bisect Bisect
|
||||||
}
|
}
|
||||||
@ -719,6 +722,8 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
LcUndo: "undo",
|
LcUndo: "undo",
|
||||||
LcUndoReflog: "undo (via reflog) (experimental)",
|
LcUndoReflog: "undo (via reflog) (experimental)",
|
||||||
LcRedoReflog: "redo (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",
|
LcPop: "pop",
|
||||||
LcDrop: "drop",
|
LcDrop: "drop",
|
||||||
LcApply: "apply",
|
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.",
|
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'?",
|
CheckoutPrompt: "Are you sure you want to checkout '%s'?",
|
||||||
UpstreamGone: "(upstream gone)",
|
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{
|
Actions: Actions{
|
||||||
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
||||||
CheckoutCommit: "Checkout commit",
|
CheckoutCommit: "Checkout commit",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user