2018-08-14 11:05:26 +02:00
|
|
|
package gui
|
2018-05-26 05:23:39 +02:00
|
|
|
|
|
|
|
import (
|
2018-07-21 07:51:18 +02:00
|
|
|
"fmt"
|
2018-05-26 05:23:39 +02:00
|
|
|
|
2018-07-21 07:51:18 +02:00
|
|
|
"github.com/jesseduffield/gocui"
|
2022-08-06 10:50:52 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
2022-06-13 03:01:26 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
2023-03-10 14:35:19 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/tasks"
|
2018-08-19 13:20:50 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
2018-08-16 07:53:53 +02:00
|
|
|
"github.com/spkg/bom"
|
2018-05-26 05:23:39 +02:00
|
|
|
)
|
|
|
|
|
2018-08-14 11:05:26 +02:00
|
|
|
func (gui *Gui) resetOrigin(v *gocui.View) error {
|
2019-04-25 21:37:19 +02:00
|
|
|
_ = v.SetCursor(0, 0)
|
2018-07-21 07:51:18 +02:00
|
|
|
return v.SetOrigin(0, 0)
|
2018-06-09 11:06:33 +02:00
|
|
|
}
|
|
|
|
|
2023-03-10 14:35:19 +02:00
|
|
|
// Returns the number of lines that we should read initially from a cmd task so
|
|
|
|
// that the scrollbar has the correct size, along with the number of lines after
|
|
|
|
// which the view is filled and we can do a first refresh.
|
|
|
|
func (gui *Gui) linesToReadFromCmdTask(v *gocui.View) tasks.LinesToRead {
|
|
|
|
_, height := v.Size()
|
|
|
|
_, oy := v.Origin()
|
|
|
|
|
|
|
|
linesForFirstRefresh := height + oy + 10
|
|
|
|
|
|
|
|
// We want to read as many lines initially as necessary to let the
|
|
|
|
// scrollbar go to its minimum height, so that the scrollbar thumb doesn't
|
|
|
|
// change size as you scroll down.
|
|
|
|
minScrollbarHeight := 2
|
|
|
|
linesToReadForAccurateScrollbar := height*(height-1)/minScrollbarHeight + oy
|
|
|
|
|
|
|
|
// However, cap it at some arbitrary max limit, so that we don't get
|
|
|
|
// performance problems for huge monitors or tiny font sizes
|
|
|
|
if linesToReadForAccurateScrollbar > 5000 {
|
|
|
|
linesToReadForAccurateScrollbar = 5000
|
|
|
|
}
|
|
|
|
|
|
|
|
return tasks.LinesToRead{
|
|
|
|
Total: linesToReadForAccurateScrollbar,
|
|
|
|
InitialRefreshAfter: linesForFirstRefresh,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 11:12:31 +02:00
|
|
|
func (gui *Gui) cleanString(s string) string {
|
2018-12-12 13:34:20 +02:00
|
|
|
output := string(bom.Clean([]byte(s)))
|
2019-01-15 11:12:31 +02:00
|
|
|
return utils.NormalizeLinefeeds(output)
|
|
|
|
}
|
|
|
|
|
2021-04-11 02:05:19 +02:00
|
|
|
func (gui *Gui) setViewContent(v *gocui.View, s string) {
|
2022-01-15 03:04:00 +02:00
|
|
|
v.SetContent(gui.cleanString(s))
|
2021-04-11 02:05:19 +02:00
|
|
|
}
|
|
|
|
|
2019-01-15 11:12:31 +02:00
|
|
|
// renderString resets the origin of a view and sets its content
|
2022-01-15 03:04:00 +02:00
|
|
|
func (gui *Gui) renderString(view *gocui.View, s string) error {
|
2021-04-04 16:31:52 +02:00
|
|
|
if err := view.SetOrigin(0, 0); err != nil {
|
2020-08-16 10:25:08 +02:00
|
|
|
return err
|
|
|
|
}
|
2021-04-04 16:31:52 +02:00
|
|
|
if err := view.SetCursor(0, 0); err != nil {
|
2020-08-16 10:25:08 +02:00
|
|
|
return err
|
|
|
|
}
|
2022-01-15 03:04:00 +02:00
|
|
|
gui.setViewContent(view, s)
|
2020-08-16 10:25:08 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-02-25 13:11:35 +02:00
|
|
|
func (gui *Gui) currentViewName() string {
|
|
|
|
currentView := gui.g.CurrentView()
|
2020-05-16 04:35:19 +02:00
|
|
|
if currentView == nil {
|
|
|
|
return ""
|
|
|
|
}
|
2018-08-14 11:05:26 +02:00
|
|
|
return currentView.Name()
|
|
|
|
}
|
2018-09-05 11:07:46 +02:00
|
|
|
|
2020-08-15 09:23:16 +02:00
|
|
|
func (gui *Gui) resizeCurrentPopupPanel() error {
|
|
|
|
v := gui.g.CurrentView()
|
2020-08-18 14:41:14 +02:00
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2022-05-08 04:46:48 +02:00
|
|
|
|
|
|
|
if v == gui.Views.Menu {
|
|
|
|
gui.resizeMenu()
|
2022-05-08 05:14:24 +02:00
|
|
|
} else if v == gui.Views.Confirmation || v == gui.Views.Suggestions {
|
|
|
|
gui.resizeConfirmationPanel()
|
2022-05-08 04:46:48 +02:00
|
|
|
} else if gui.isPopupPanel(v.Name()) {
|
2021-10-17 04:00:44 +02:00
|
|
|
return gui.resizePopupPanel(v, v.Buffer())
|
2018-09-05 11:07:46 +02:00
|
|
|
}
|
2022-05-08 04:46:48 +02:00
|
|
|
|
2018-09-05 11:07:46 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-17 04:00:44 +02:00
|
|
|
func (gui *Gui) resizePopupPanel(v *gocui.View, content string) error {
|
2020-08-15 09:23:16 +02:00
|
|
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(v.Wrap, content)
|
|
|
|
_, err := gui.g.SetView(v.Name(), x0, y0, x1, y1, 0)
|
2018-09-05 11:07:46 +02:00
|
|
|
return err
|
|
|
|
}
|
2018-12-04 10:50:11 +02:00
|
|
|
|
2022-05-08 05:14:24 +02:00
|
|
|
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()
|
2022-09-01 12:31:40 +02:00
|
|
|
wrap := true
|
|
|
|
if gui.Views.Confirmation.Editable {
|
|
|
|
prompt = gui.Views.Confirmation.TextArea.GetContent()
|
|
|
|
wrap = false
|
|
|
|
}
|
2022-08-18 16:05:30 +02:00
|
|
|
panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth) + suggestionsViewHeight
|
2022-05-08 05:14:24 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-08-23 02:50:27 +02:00
|
|
|
func (gui *Gui) globalOptionsMap() map[string]string {
|
2022-01-16 05:46:53 +02:00
|
|
|
keybindingConfig := gui.c.UserConfig.Keybinding
|
2020-10-03 06:54:55 +02:00
|
|
|
|
2020-08-23 02:50:27 +02:00
|
|
|
return map[string]string{
|
2022-08-06 10:50:52 +02:00
|
|
|
fmt.Sprintf("%s/%s", keybindings.Label(keybindingConfig.Universal.ScrollUpMain), keybindings.Label(keybindingConfig.Universal.ScrollDownMain)): gui.c.Tr.LcScroll,
|
|
|
|
fmt.Sprintf("%s %s %s %s", keybindings.Label(keybindingConfig.Universal.PrevBlock), keybindings.Label(keybindingConfig.Universal.NextBlock), keybindings.Label(keybindingConfig.Universal.PrevItem), keybindings.Label(keybindingConfig.Universal.NextItem)): gui.c.Tr.LcNavigate,
|
2023-02-19 04:13:28 +02:00
|
|
|
keybindings.Label(keybindingConfig.Universal.Return): gui.c.Tr.LcCancel,
|
|
|
|
keybindings.Label(keybindingConfig.Universal.Quit): gui.c.Tr.LcQuit,
|
|
|
|
keybindings.Label(keybindingConfig.Universal.OptionMenuAlt1): gui.c.Tr.LcMenu,
|
2022-08-06 10:50:52 +02:00
|
|
|
fmt.Sprintf("%s-%s", keybindings.Label(keybindingConfig.Universal.JumpToBlock[0]), keybindings.Label(keybindingConfig.Universal.JumpToBlock[len(keybindingConfig.Universal.JumpToBlock)-1])): gui.c.Tr.LcJump,
|
|
|
|
fmt.Sprintf("%s/%s", keybindings.Label(keybindingConfig.Universal.ScrollLeft), keybindings.Label(keybindingConfig.Universal.ScrollRight)): gui.c.Tr.LcScrollLeftRight,
|
2020-08-23 02:50:27 +02:00
|
|
|
}
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2019-03-11 04:04:08 +02:00
|
|
|
func (gui *Gui) isPopupPanel(viewName string) bool {
|
2022-02-23 10:44:48 +02:00
|
|
|
return viewName == "commitMessage" || viewName == "confirmation" || viewName == "menu"
|
2019-03-11 04:04:08 +02:00
|
|
|
}
|
|
|
|
|
2019-02-25 13:11:35 +02:00
|
|
|
func (gui *Gui) popupPanelFocused() bool {
|
2019-03-11 04:04:08 +02:00
|
|
|
return gui.isPopupPanel(gui.currentViewName())
|
2019-02-25 13:11:35 +02:00
|
|
|
}
|
2019-11-10 07:20:35 +02:00
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
func (gui *Gui) onViewTabClick(windowName string, tabIndex int) error {
|
|
|
|
tabs := gui.viewTabMap()[windowName]
|
|
|
|
if len(tabs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-08-17 13:58:30 +02:00
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
viewName := tabs[tabIndex].ViewName
|
|
|
|
|
|
|
|
context, ok := gui.contextForView(viewName)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
2020-08-19 11:31:58 +02:00
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
return gui.c.PushContext(context)
|
2020-08-19 11:31:58 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
func (gui *Gui) contextForView(viewName string) (types.Context, bool) {
|
|
|
|
view, err := gui.g.View(viewName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, context := range gui.State.Contexts.Flatten() {
|
|
|
|
if context.GetViewName() == view.Name() {
|
|
|
|
return context, true
|
|
|
|
}
|
2021-04-02 10:20:40 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
return nil, false
|
2020-08-19 11:31:58 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
func (gui *Gui) handleNextTab() error {
|
|
|
|
view := getTabbedView(gui)
|
|
|
|
if view == nil {
|
2021-04-02 10:20:40 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
for _, context := range gui.State.Contexts.Flatten() {
|
|
|
|
if context.GetViewName() == view.Name() {
|
|
|
|
return gui.onViewTabClick(
|
|
|
|
context.GetWindowName(),
|
|
|
|
utils.ModuloWithWrap(view.TabIndex+1, len(view.Tabs)),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-08-19 11:31:58 +02:00
|
|
|
}
|
2020-10-01 23:56:14 +02:00
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
func (gui *Gui) handlePrevTab() error {
|
|
|
|
view := getTabbedView(gui)
|
|
|
|
if view == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2020-10-01 23:56:14 +02:00
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
for _, context := range gui.State.Contexts.Flatten() {
|
|
|
|
if context.GetViewName() == view.Name() {
|
|
|
|
return gui.onViewTabClick(
|
|
|
|
context.GetWindowName(),
|
|
|
|
utils.ModuloWithWrap(view.TabIndex-1, len(view.Tabs)),
|
|
|
|
)
|
|
|
|
}
|
2020-10-01 23:56:14 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
return nil
|
2020-10-01 23:56:14 +02:00
|
|
|
}
|
2021-06-06 12:41:55 +02:00
|
|
|
|
|
|
|
func getTabbedView(gui *Gui) *gocui.View {
|
2021-06-15 19:58:43 +02:00
|
|
|
// It safe assumption that only static contexts have tabs
|
|
|
|
context := gui.currentStaticContext()
|
|
|
|
view, _ := gui.g.View(context.GetViewName())
|
|
|
|
return view
|
2021-06-06 12:41:55 +02:00
|
|
|
}
|
2021-11-01 00:35:54 +02:00
|
|
|
|
|
|
|
func (gui *Gui) render() {
|
2022-08-06 10:50:52 +02:00
|
|
|
gui.c.OnUIThread(func() error { return nil })
|
2021-11-01 00:35:54 +02:00
|
|
|
}
|