summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/gui/arrangement.go142
-rw-r--r--pkg/gui/confirmation_panel.go16
-rw-r--r--pkg/gui/context/menu_context.go15
-rw-r--r--pkg/gui/controllers/undo_controller.go2
-rw-r--r--pkg/gui/controllers/workspace_reset_controller.go3
-rw-r--r--pkg/gui/gui.go158
-rw-r--r--pkg/gui/layout.go78
-rw-r--r--pkg/gui/list_context_config.go6
-rw-r--r--pkg/gui/menu_panel.go16
-rw-r--r--pkg/gui/options_menu_panel.go3
-rw-r--r--pkg/gui/types/common.go3
-rw-r--r--pkg/gui/types/keybindings.go3
-rw-r--r--pkg/gui/view_helpers.go43
-rw-r--r--pkg/gui/views.go218
-rw-r--r--pkg/i18n/english.go6
15 files changed, 382 insertions, 330 deletions
diff --git a/pkg/gui/arrangement.go b/pkg/gui/arrangement.go
index d0e4c7fd0..5ab8ab5dd 100644
--- a/pkg/gui/arrangement.go
+++ b/pkg/gui/arrangement.go
@@ -13,6 +13,84 @@ import (
const INFO_SECTION_PADDING = " "
+func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
+ width, height := gui.g.Size()
+
+ sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights()
+
+ sidePanelsDirection := boxlayout.COLUMN
+ portraitMode := width <= 84 && height > 45
+ if portraitMode {
+ sidePanelsDirection = boxlayout.ROW
+ }
+
+ mainPanelsDirection := boxlayout.ROW
+ if gui.splitMainPanelSideBySide() {
+ mainPanelsDirection = boxlayout.COLUMN
+ }
+
+ extrasWindowSize := gui.getExtrasWindowSize(height)
+
+ showInfoSection := gui.c.UserConfig.Gui.ShowBottomLine || (gui.State.Searching.isSearching || gui.isAnyModeActive())
+ infoSectionSize := 0
+ if showInfoSection {
+ infoSectionSize = 1
+ }
+
+ root := &boxlayout.Box{
+ Direction: boxlayout.ROW,
+ Children: []*boxlayout.Box{
+ {
+ Direction: sidePanelsDirection,
+ Weight: 1,
+ Children: []*boxlayout.Box{
+ {
+ Direction: boxlayout.ROW,
+ Weight: sideSectionWeight,
+ ConditionalChildren: gui.sidePanelChildren,
+ },
+ {
+ Direction: boxlayout.ROW,
+ Weight: mainSectionWeight,
+ Children: []*boxlayout.Box{
+ {
+ Direction: mainPanelsDirection,
+ Children: gui.mainSectionChildren(),
+ Weight: 1,
+ },
+ {
+ Window: "extras",
+ Size: extrasWindowSize,
+ },
+ },
+ },
+ },
+ },
+ {
+ Direction: boxlayout.COLUMN,
+ Size: infoSectionSize,
+ Children: gui.infoSectionChildren(informationStr, appStatus),
+ },
+ },
+ }
+
+ layerOneWindows := boxlayout.ArrangeWindows(root, 0, 0, width, height)
+ limitWindows := boxlayout.ArrangeWindows(&boxlayout.Box{Window: "limit"}, 0, 0, width, height)
+
+ return MergeMaps(layerOneWindows, limitWindows)
+}
+
+func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
+ result := map[K]V{}
+ for _, currMap := range maps {
+ for key, value := range currMap {
+ result[key] = value
+ }
+ }
+
+ return result
+}
+
func (gui *Gui) mainSectionChildren() []*boxlayout.Box {
currentWindow := gui.currentWindow()
@@ -156,70 +234,6 @@ func (gui *Gui) getExtrasWindowSize(screenHeight int) int {
return baseSize + frameSize
}
-func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
- width, height := gui.g.Size()
-
- sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights()
-
- sidePanelsDirection := boxlayout.COLUMN
- portraitMode := width <= 84 && height > 45
- if portraitMode {
- sidePanelsDirection = boxlayout.ROW
- }
-
- mainPanelsDirection := boxlayout.ROW
- if gui.splitMainPanelSideBySide() {
- mainPanelsDirection = boxlayout.COLUMN
- }
-
- extrasWindowSize := gui.getExtrasWindowSize(height)
-
- showInfoSection := gui.c.UserConfig.Gui.ShowBottomLine || (gui.State.Searching.isSearching || gui.isAnyModeActive())
- infoSectionSize := 0
- if showInfoSection {
- infoSectionSize = 1
- }
-
- root := &boxlayout.Box{
- Direction: boxlayout.ROW,
- Children: []*boxlayout.Box{
- {
- Direction: sidePanelsDirection,
- Weight: 1,
- Children: []*boxlayout.Box{
- {
- Direction: boxlayout.ROW,
- Weight: sideSectionWeight,
- ConditionalChildren: gui.sidePanelChildren,
- },
- {
- Direction: boxlayout.ROW,
- Weight: mainSectionWeight,
- Children: []*boxlayout.Box{
- {
- Direction: mainPanelsDirection,
- Children: gui.mainSectionChildren(),
- Weight: 1,
- },
- {
- Window: "extras",
- Size: extrasWindowSize,
- },
- },
- },
- },
- },
- {
- Direction: boxlayout.COLUMN,
- Size: infoSectionSize,
- Children: gui.infoSectionChildren(informationStr, appStatus),
- },
- },
- }
-
- return boxlayout.ArrangeWindows(root, 0, 0, width, height)
-}
-
// The stash window by default only contains one line so that it's not hogging
// too much space, but if you access it it should take up some space. This is
// the default behaviour when accordion mode is NOT in effect. If it is in effect
diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go
index b08fcba44..5d8a13bb7 100644
--- a/pkg/gui/confirmation_panel.go
+++ b/pkg/gui/confirmation_panel.go
@@ -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)
}
@@ -131,13 +130,6 @@ func (gui *Gui) prepareConfirmationPanel(
editable bool,
mask bool,
) error {
- x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
- // calling SetView on an existing view returns the same view, so I'm not bothering
- // to reassign to gui.Views.Confirmation
- _, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
- if err != nil {
- return err
- }
gui.Views.Confirmation.HasLoader = hasLoader
if hasLoader {
gui.g.StartTicking()
@@ -150,11 +142,7 @@ func (gui *Gui) prepareConfirmationPanel(
gui.findSuggestions = findSuggestionsFunc
if findSuggestionsFunc != nil {
- suggestionsViewHeight := 11
- suggestionsView, err := gui.g.SetView("suggestions", x0, y1+1, x1, y1+suggestionsViewHeight, 0)
- if err != nil {
- return err
- }
+ suggestionsView := gui.Views.Suggestions
suggestionsView.Wrap = false
suggestionsView.FgColor = theme.GocuiDefaultTextColor
gui.setSuggestions(findSuggestionsFunc(""))
diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go
index 376a3ae4b..be748eb70 100644
--- a/pkg/gui/context/menu_context.go
+++ b/pkg/gui/context/menu_context.go
@@ -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,
diff --git a/pkg/gui/controllers/undo_controller.go b/pkg/gui/controllers/undo_controller.go
index 5e4c2043b..5e0bf5730 100644
--- a/pkg/gui/controllers/undo_controller.go
+++ b/pkg/gui/controllers/undo_controller.go
@@ -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,
},
}
diff --git a/pkg/gui/controllers/workspace_reset_controller.go b/pkg/gui/controllers/workspace_reset_controller.go
index 32dd959df..bd5f47195 100644
--- a/pkg/gui/controllers/workspace_reset_controller.go
+++ b/pkg/gui/controllers/workspace_reset_controller.go
@@ -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{
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index f2dd1c3f4..a75ef897c 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -223,30 +223,6 @@ type panelStates struct {
Merging *MergingPanelState
}
-type Views struct {
- Status *gocui.View
- Files *gocui.View
- Branches *gocui.View
- RemoteBranches *gocui.View
- Commits *gocui.View
- Stash *gocui.View
- Main *gocui.View
- Secondary *gocui.View
- Options *gocui.View
- Confirmation *gocui.View
- Menu *gocui.View
- CommitMessage *gocui.View
- CommitFiles *gocui.View
- SubCommits *gocui.View
- Information *gocui.View
- AppStatus *gocui.View
- Search *gocui.View
- SearchPrefix *gocui.View
- Limit *gocui.View
- Suggestions *gocui.View
- Extras *gocui.View
-}
-
type searchingState struct {
view *gocui.View
isSearching bool
@@ -389,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(
@@ -596,121 +553,6 @@ func (gui *Gui) Run(filterPath string) error {
return gui.g.MainLoop()
}
-func (gui *Gui) createAllViews() error {
- viewNameMappings := []struct {
- viewPtr **gocui.View
- name string
- }{
- {viewPtr: &gui.Views.Status, name: "status"},
- {viewPtr: &gui.Views.Files, name: "files"},
- {viewPtr: &gui.Views.Branches, name: "branches"},
- {viewPtr: &gui.Views.RemoteBranches, name: "remoteBranches"},
- {viewPtr: &gui.Views.Commits, name: "commits"},
- {viewPtr: &gui.Views.Stash, name: "stash"},
- {viewPtr: &gui.Views.CommitFiles, name: "commitFiles"},
- {viewPtr: &gui.Views.SubCommits, name: "subCommits"},
- {viewPtr: &gui.Views.Main, name: "main"},
- {viewPtr: &gui.Views.Secondary, name: "secondary"},
- {viewPtr: &gui.Views.Options, name: "options"},
- {viewPtr: &gui.Views.AppStatus, name: "appStatus"},
- {viewPtr: &gui.Views.Information, name: "information"},
- {viewPtr: &gui.Views.Search, name: "search"},
- {viewPtr: &gui.Views.SearchPrefix, name: "searchPrefix"},
- {viewPtr: &gui.Views.CommitMessage, name: "commitMessage"},
- {viewPtr: &gui.Views.Menu, name: "menu"},
- {viewPtr: &gui.Views.Suggestions, name: "suggestions"},
- {viewPtr: &gui.Views.Confirmation, name: "confirmation"},
- {viewPtr: &gui.Views.Limit, name: "limit"},
- {viewPtr: &gui.Views.Extras, name: "extras"},
- }
-
- var err error
- for _, mapping := range viewNameMappings {
- *mapping.viewPtr, err = gui.prepareView(mapping.name)
- if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
- return err
- }
- }
-
- gui.Views.Options.Frame = false
- gui.Views.Options.FgColor = theme.OptionsColor
-
- gui.Views.SearchPrefix.BgColor = gocui.ColorDefault
- gui.Views.SearchPrefix.FgColor = gocui.ColorGreen
- gui.Views.SearchPrefix.Frame = false
- gui.setViewContent(gui.Views.SearchPrefix, SEARCH_PREFIX)
-
- gui.Views.Stash.Title = gui.c.Tr.StashTitle
- gui.Views.Stash.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.Commits.Title = gui.c.Tr.CommitsTitle
- gui.Views.Commits.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.CommitFiles.Title = gui.c.Tr.CommitFiles
- gui.Views.CommitFiles.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.SubCommits.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.Branches.Title = gui.c.Tr.BranchesTitle
- gui.Views.Branches.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.RemoteBranches.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.Files.Title = gui.c.Tr.FilesTitle
- gui.Views.Files.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.Secondary.Title = gui.c.Tr.DiffTitle
- gui.Views.Secondary.Wrap = true
- gui.Views.Secondary.FgColor = theme.GocuiDefaultTextColor
- gui.Views.Secondary.IgnoreCarriageReturns = true
- gui.Views.Secondary.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom
-
- gui.Views.Main.Title = gui.c.Tr.DiffTitle
- gui.Views.Main.Wrap = true
- gui.Views.Main.FgColor = theme.GocuiDefaultTextColor
- gui.Views.Main.IgnoreCarriageReturns = true
- gui.Views.Main.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom
-
- gui.Views.Limit.Title = gui.c.Tr.NotEnoughSpace
- gui.Views.Limit.Wrap = true
-
- gui.Views.Status.Title = gui.c.Tr.StatusTitle
- gui.Views.Status.FgColor = theme.GocuiDefaultTextColor
-
- gui.Views.Search.BgColor = gocui.ColorDefault
- gui.Views.Search.FgColor = gocui.ColorGreen
- gui.Views.Search.Frame = false
- gui.Views.Search.Editable = true
-
- gui.Views.AppStatus.BgColor = gocui.ColorDefault
- gui.Views.AppStatus.FgColor = gocui.ColorCyan
- gui.Views.AppStatus.Frame = false
- gui.Views.AppStatus.Visible = false
-
- gui.Views.CommitMessage.Visible = false
- gui.Views.CommitMessage.Title = gui.c.Tr.CommitMessage
- gui.Views.CommitMessage.FgColor = theme.GocuiDefaultTextColor
- gui.Views.CommitMessage.Editable = true
- gui.Views.CommitMessage.Editor = gocui.EditorFunc(gui.commitMessageEditor)
-
- gui.Views.Confirmation.Visible = false
-
- gui.Views.Suggestions.Visible = false
-
- gui.Views.Menu.Visible = false
-
- gui.Views.Information.BgColor = gocui.ColorDefault
- gui.Views.Information.FgColor = gocui.ColorGreen
- gui.Views.Information.Frame = false
-
- gui.Views.Extras.Title = gui.c.Tr.CommandLog
- gui.Views.Extras.FgColor = theme.GocuiDefaultTextColor
- gui.Views.Extras.Autoscroll = true
- gui.Views.Extras.Wrap = true
-
- return nil
-}
-
func (gui *Gui) RunAndHandleError(filterPath string) error {
gui.stopChan = make(chan struct{})
return utils.SafeWithError(func() error {
diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go
index 2fb165ff4..bd36174bd 100644
--- a/pkg/gui/layout.go
+++ b/pkg/gui/layout.go
@@ -20,15 +20,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
g.Highlight = true
width, height := g.Size()
- minimumHeight := 9
- minimumWidth := 10
- var err error
- _, err = g.SetView("limit", 0, 0, width-1, height-1, 0)
- if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
- return err
- }
- gui.Views.Limit.Visible = height < minimumHeight || width < minimumWidth
-
informationStr := gui.informationStr()
appStatus := gui.statusManager.getStatusString()
@@ -77,6 +68,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
dimensionsObj.Y1+frameOffset,
0,
)
+ view.Frame = frame
if view != nil {
view.Visible = true
@@ -85,36 +77,19 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return view, err
}
- args := []struct {
- viewName string
- windowName string
- frame bool
- }{
- {viewName: "main", windowName: "main", frame: true},
- {viewName: "secondary", windowName: "secondary", frame: true},
- {viewName: "status", windowName: "status", frame: true},
- {viewName: "files", windowName: "files", frame: true},
- {viewName: "branches", windowName: "branches", frame: true},
- {viewName: "remoteBranches", windowName: "branches", frame: true},
- {viewName: "commitFiles", windowName: gui.State.Contexts.CommitFiles.GetWindowName(), frame: true},
- {viewName: "subCommits", windowName: gui.State.Contexts.SubCommits.GetWindowName(), frame: true},
- {viewName: "commits", windowName: "commits", frame: true},
- {viewName: "stash", windowName: "stash", frame: true},
- {viewName: "options", windowName: "options", frame: false},
- {viewName: "searchPrefix", windowName: "searchPrefix", frame: false},
- {viewName: "search", windowName: "search", frame: false},
- {viewName: "appStatus", windowName: "appStatus", frame: false},
- {viewName: "information", windowName: "information", frame: false},
- {viewName: "extras", windowName: "extras", frame: true},
- }
-
- for _, arg := range args {
- _, err = setViewFromDimensions(arg.viewName, arg.windowName, arg.frame)
+ for _, arg := range gui.controlledViews() {
+ _, err := setViewFromDimensions(arg.viewName, arg.windowName, arg.frame)
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
return err
}
}
+ minimumHeight := 9
+ 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 {
@@ -205,40 +180,7 @@ func (gui *Gui) onInitialViewsCreationForRepo() error {
func (gui *Gui) onInitialViewsCreation() error {
// now we order the views (in order of bottom first)
- layerOneViews := []*gocui.View{
- // first layer. Ordering within this layer does not matter because there are
- // no overlapping views
- gui.Views.Status,
- gui.Views.Files,
- gui.Views.Branches,
- gui.Views.RemoteBranches,
- gui.Views.Commits,
- gui.Views.Stash,
- gui.Views.SubCommits,
- gui.Views.CommitFiles,
- gui.Views.Main,
- gui.Views.Secondary,
- gui.Views.Extras,
-
- // bottom line
- gui.Views.Options,
- gui.Views.AppStatus,
- gui.Views.Information,
- gui.Views.Search,
- gui.Views.SearchPrefix, // this view takes up one character. Its only purpose is to show the slash when searching
-
- // popups. Ordering within this layer does not matter because there should
- // only be one popup shown at a time
- gui.Views.CommitMessage,
- gui.Views.Menu,
- gui.Views.Suggestions,
- gui.Views.Confirmation,
-
- // this guy will cover everything else when it appears
- gui.Views.Limit,
- }
-
- for _, view := range layerOneViews {
+ for _, view := range gui.orderedViews() {
if _, err := gui.g.SetViewOnTop(view.Name()); err != nil {
return err
}
diff --git a/pkg/gui/list_context_config.go b/pkg/gui/list_context_config.go
index e8a65d6da..5fe710055 100644
--- a/pkg/gui/list_context_config.go
+++ b/pkg/gui/list_context_config.go
@@ -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)
+ },
)
}
diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go
index 8bce02011..459fd60ce 100644
--- a/pkg/gui/menu_panel.go
+++ b/pkg/gui/menu_panel.go
@@ -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 {
diff --git a/pkg/gui/options_menu_panel.go b/pkg/gui/options_menu_panel.go
index 846d1de16..f7baf10ad 100644
--- a/pkg/gui/options_menu_panel.go
+++ b/pkg/gui/options_menu_panel.go
@@ -73,7 +73,8 @@ func (gui *Gui) handleCreateOptionsMenu() error {
return binding.Handler()
},
- Key: binding.Key,
+ Key: binding.Key,
+ Tooltip: binding.Tooltip,
}
})
diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go
index 277732a34..fc2eebf59 100644
--- a/pkg/gui/types/common.go
+++ b/pkg/gui/types/common.go
@@ -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 {
diff --git a/pkg/gui/types/keybindings.go b/pkg/gui/types/keybindings.go
index b4db46336..a257fcfc2 100644
--- a/pkg/gui/types/keybindings.go
+++ b/pkg/gui/types/keybindings.go
@@ -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
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index dfb77e1e2..023a1b41c 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -47,24 +47,53 @@ func (gui *Gui) resizeCurrentPopupPanel() error {
if v == nil {
return nil
}
- if gui.isPopupPanel(v.Name()) {
+
+ if v == gui.Views.Menu {
+ gui.resizeMenu()
+ } else if v == gui.Views.Confirmation || v == gui.Views.Suggestions {
+ gui.resizeConfirmationPanel()
+ } else if gui.isPopupPanel(v.Name()) {
return gui.resizePopupPanel(v, v.Buffer())
}
+
return nil
}
func (gui *Gui) resizePopupPanel(v *gocui.View, content string) error {
- // If the confirmation panel is already displayed, just resize the width,
- // otherwise continue
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(v.Wrap, content)
- vx0, vy0, vx1, vy1 := v.Dimensions()
- if vx0 == x0 && vy0 == y0 && vx1 == x1 && vy1 == y1 {
- return nil
- }
_, err := gui.g.SetView(v.Name(), x0, y0, x1, y1, 0)
return err
}
+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(gui.Views.Menu.Name(), 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(gui.Views.Tooltip.Name(), x0, tooltipTop, x1, tooltipTop+tooltipHeight-1, 0)
+}
+
+func (gui *Gui) resizeConfirmationPanel() {
+ suggestionsViewHeight := 0
+ if gui.Views.Suggestions.Visible {
+ suggestionsViewHeight = 11
+ }
+ panelWidth := gui.getConfirmationPanelWidth()
+ prompt := gui.Views.Confirmation.Buffer()
+ panelHeight := gui.getMessageHeight(true, prompt, panelWidth) + suggestionsViewHeight
+ x0, y0, x1, y1 := gui.getConfirmationPanelDimensionsAux(panelWidth, panelHeight)
+ confirmationViewBottom := y1 - suggestionsViewHeight
+ _, _ = gui.g.SetView(gui.Views.Confirmation.Name(), x0, y0, x1, confirmationViewBottom, 0)
+
+ suggestionsViewTop := confirmationViewBottom + 1
+ _, _ = gui.g.SetView(gui.Views.Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
+}
+
func (gui *Gui) globalOptionsMap() map[string]string {
keybindingConfig := gui.c.UserConfig.Keybinding
diff --git a/pkg/gui/views.go b/pkg/gui/views.go
new file mode 100644
index 000000000..2da245507
--- /dev/null
+++ b/pkg/gui/views.go
@@ -0,0 +1,218 @@
+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"
+)
+
+type Views struct {
+ Status *gocui.View
+ Files *gocui.View
+ Branches *gocui.View
+ RemoteBranches *gocui.View
+ Commits *gocui.View
+ Stash *gocui.View
+ Main *gocui.View
+ Secondary *gocui.View
+ Options *gocui.View
+ Confirmation *gocui.View
+ Menu *gocui.View
+ CommitMessage *gocui.View
+ CommitFiles *gocui.View
+ SubCommits *gocui.View
+ Information *gocui.View
+ AppStatus *gocui.View
+ Search *gocui.View
+ SearchPrefix *gocui.View
+ Limit *gocui.View
+ Suggestions *gocui.View
+ Tooltip *gocui.View
+ Extras *gocui.View
+}
+
+type viewNameMapping struct {
+ viewPtr **gocui.View
+ name string
+}
+
+func (gui *Gui) orderedViews() []*gocui.View {
+ return slices.Map(gui.orderedViewNameMappings(), func(v viewNameMapping) *gocui.View {
+ return *v.viewPtr
+ })
+}
+
+func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
+ return []viewNameMapping{
+ // first layer. Ordering within this layer does not matter because there are
+ // no overlapping views
+ {viewPtr: &gui.Views.Status, name: "status"},
+ {viewPtr: &gui.Views.Files, name: "files"},
+ {viewPtr: &gui.Views.Branches, name: "branches"},
+ {viewPtr: &gui.Views.RemoteBranches, name: "remoteBranches"},
+ {viewPtr: &gui.Views.Commits, name: "commits"},
+ {viewPtr: &gui.Views.Stash, name: "stash"},
+ {viewPtr: &gui.Views.SubCommits, name: "subCommits"},
+ {viewPtr: &gui.Views.CommitFiles, name: "commitFiles"},
+ {viewPtr: &gui.Views.Main, name: "main"},
+ {viewPtr: &gui.Views.Secondary, name: "secondary"},
+ {viewPtr: &gui.Views.Extras, name: "extras"},
+
+ // bottom line
+ {viewPtr: &gui.Views.Options, name: "options"},
+ {viewPtr: &gui.Views.AppStatus, name: "appStatus"},
+ {viewPtr: &gui.Views.Information, name: "information"},
+ {viewPtr: &gui.Views.Search, name: "search"},
+ // this view takes up one character. Its only purpose is to show the slash when searching
+ {viewPtr: &gui.Views.SearchPrefix, name: "searchPrefix"},
+
+ // popups.
+ {viewPtr: &gui.Views.CommitMessage, name: "commitMessage"},
+ {viewPtr: &gui.Views.Menu, name: "menu"},
+ {viewPtr: &gui.Views.Suggestions, name: "suggestions"},
+ {viewPtr: &gui.Views.Confirmation, name: "confirmation"},
+ {viewPtr: &gui.Views.Tooltip, name: "tooltip"},
+
+ // this guy will cover everything else when it appears
+ {viewPtr: &gui.Views.Limit, name: "limit"},
+ }
+}
+
+type controlledView struct {
+ viewName string
+ windowName string
+ frame bool
+}
+
+// controlled views have their size and position determined in arrangement.go.
+// Some views, like the confirmation panel, are currently sized at the time of
+// displaying the view, based on the view's contents.
+func (gui *Gui) controlledViews() []controlledView {
+ return []controlledView{
+ {viewName: "main", windowName: "main", frame: true},
+ {viewName: "secondary", windowName: "secondary", frame: true},
+ {viewName: "status", windowName: "status", frame: true},
+ {viewName: "files", windowName: "files", frame: true},
+ {viewName: "branches", windowName: "branches", frame: true},
+ {viewName: "remoteBranches", windowName: "branches", frame: true},
+ {viewName: "commitFiles", windowName: gui.State.Contexts.CommitFiles.GetWindowName(), frame: true