2019-11-16 05:00:27 +02:00
package gui
2021-11-01 00:35:54 +02:00
import (
"fmt"
2021-11-04 00:42:34 +02:00
"github.com/jesseduffield/gocui"
2022-01-16 05:46:53 +02:00
"github.com/jesseduffield/lazygit/pkg/config"
2022-01-29 10:09:20 +02:00
"github.com/jesseduffield/lazygit/pkg/gui/context"
2022-01-16 05:46:53 +02:00
"github.com/jesseduffield/lazygit/pkg/gui/types"
2021-11-01 00:35:54 +02:00
)
2020-08-17 13:58:30 +02:00
type ListContext struct {
2022-01-16 05:46:53 +02:00
GetItemsLength func ( ) int
GetDisplayStrings func ( startIdx int , length int ) [ ] [ ] string
OnFocus func ( ... types . OnFocusOpts ) error
OnRenderToMain func ( ... types . OnFocusOpts ) error
OnFocusLost func ( ) error
2020-08-22 07:56:30 +02:00
2022-01-30 11:03:08 +02:00
OnGetSelectedItemId func ( ) string
OnGetPanelState func ( ) types . IListPanelState
2021-11-02 07:39:15 +02:00
// if this is true, we'll call GetDisplayStrings for just the visible part of the
// view and re-render that. This is useful when you need to render different
// content based on the selection (e.g. for showing the selected commit)
RenderSelection bool
2020-08-19 10:06:51 +02:00
2021-10-17 10:01:02 +02:00
Gui * Gui
2021-04-11 05:17:20 +02:00
2022-01-29 10:09:20 +02:00
* context . BaseContext
2019-11-16 05:00:27 +02:00
}
2022-01-16 05:46:53 +02:00
var _ types . IListContext = & ListContext { }
2021-11-01 00:35:54 +02:00
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) GetPanelState ( ) types . IListPanelState {
2021-11-01 00:35:54 +02:00
return self . OnGetPanelState ( )
}
func ( self * ListContext ) FocusLine ( ) {
view , err := self . Gui . g . View ( self . ViewName )
if err != nil {
2021-11-02 07:39:15 +02:00
// ignoring error for now
2021-11-01 00:35:54 +02:00
return
}
2021-11-02 07:39:15 +02:00
// we need a way of knowing whether we've rendered to the view yet.
2021-11-02 11:35:53 +02:00
view . FocusPoint ( view . OriginX ( ) , self . GetPanelState ( ) . GetSelectedLineIdx ( ) )
2021-11-02 07:39:15 +02:00
if self . RenderSelection {
_ , originY := view . Origin ( )
2022-01-26 06:42:06 +02:00
displayStrings := self . GetDisplayStrings ( originY , view . InnerHeight ( ) + 1 )
2022-01-29 10:09:20 +02:00
self . Gui . renderDisplayStringsInViewPort ( view , displayStrings )
2021-11-02 07:39:15 +02:00
}
2021-11-01 00:35:54 +02:00
view . Footer = formatListFooter ( self . GetPanelState ( ) . GetSelectedLineIdx ( ) , self . GetItemsLength ( ) )
}
func formatListFooter ( selectedLineIdx int , length int ) string {
return fmt . Sprintf ( "%d of %d" , selectedLineIdx + 1 , length )
}
2022-01-30 11:03:08 +02:00
func ( self * ListContext ) GetSelectedItemId ( ) string {
return self . OnGetSelectedItemId ( )
2020-08-22 01:55:49 +02:00
}
2020-08-19 13:51:50 +02:00
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
2021-11-21 03:48:49 +02:00
func ( self * ListContext ) HandleRender ( ) error {
2021-11-01 00:35:54 +02:00
view , err := self . Gui . g . View ( self . ViewName )
2020-08-19 13:51:50 +02:00
if err != nil {
return nil
}
2021-11-01 00:35:54 +02:00
if self . GetDisplayStrings != nil {
self . Gui . refreshSelectedLine ( self . GetPanelState ( ) , self . GetItemsLength ( ) )
self . Gui . renderDisplayStrings ( view , self . GetDisplayStrings ( 0 , self . GetItemsLength ( ) ) )
self . Gui . render ( )
2020-08-19 13:51:50 +02:00
}
return nil
}
2021-11-01 00:35:54 +02:00
func ( self * ListContext ) HandleFocusLost ( ) error {
if self . OnFocusLost != nil {
return self . OnFocusLost ( )
2020-08-16 05:58:29 +02:00
}
2021-11-02 11:35:53 +02:00
view , err := self . Gui . g . View ( self . ViewName )
if err != nil {
return nil
}
_ = view . SetOriginX ( 0 )
2020-08-16 05:58:29 +02:00
return nil
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleFocus ( opts ... types . OnFocusOpts ) error {
2021-11-01 00:35:54 +02:00
self . FocusLine ( )
2020-08-22 00:49:02 +02:00
2021-11-01 00:35:54 +02:00
if self . OnFocus != nil {
2021-11-21 03:48:49 +02:00
if err := self . OnFocus ( opts ... ) ; err != nil {
return err
}
2020-08-19 14:27:31 +02:00
}
2021-11-21 03:48:49 +02:00
if self . OnRenderToMain != nil {
if err := self . OnRenderToMain ( opts ... ) ; err != nil {
return err
}
}
2020-08-16 05:58:29 +02:00
2021-11-21 03:48:49 +02:00
return nil
2020-08-19 10:06:51 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandlePrevLine ( ) error {
2021-11-01 00:35:54 +02:00
return self . handleLineChange ( - 1 )
2019-11-16 05:00:27 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleNextLine ( ) error {
2021-11-01 00:35:54 +02:00
return self . handleLineChange ( 1 )
2019-11-16 05:00:27 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleScrollLeft ( ) error {
2021-11-04 00:42:34 +02:00
return self . scroll ( self . Gui . scrollLeft )
2021-11-02 11:35:53 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleScrollRight ( ) error {
2021-11-04 00:42:34 +02:00
return self . scroll ( self . Gui . scrollRight )
}
func ( self * ListContext ) scroll ( scrollFunc func ( * gocui . View ) ) error {
2021-11-02 11:35:53 +02:00
if self . ignoreKeybinding ( ) {
return nil
}
// get the view, move the origin
view , err := self . Gui . g . View ( self . ViewName )
if err != nil {
return nil
}
2021-11-04 00:42:34 +02:00
scrollFunc ( view )
2021-11-02 11:35:53 +02:00
return self . HandleFocus ( )
}
func ( self * ListContext ) ignoreKeybinding ( ) bool {
return ! self . Gui . isPopupPanel ( self . ViewName ) && self . Gui . popupPanelFocused ( )
}
2021-11-01 00:35:54 +02:00
func ( self * ListContext ) handleLineChange ( change int ) error {
2021-11-02 11:35:53 +02:00
if self . ignoreKeybinding ( ) {
2019-11-16 05:00:27 +02:00
return nil
}
2021-11-01 00:35:54 +02:00
selectedLineIdx := self . GetPanelState ( ) . GetSelectedLineIdx ( )
if ( change < 0 && selectedLineIdx == 0 ) || ( change > 0 && selectedLineIdx == self . GetItemsLength ( ) - 1 ) {
2020-10-01 13:40:20 +02:00
return nil
}
2021-11-01 00:35:54 +02:00
self . Gui . changeSelectedLine ( self . GetPanelState ( ) , self . GetItemsLength ( ) , change )
2019-11-16 05:00:27 +02:00
2021-11-01 00:35:54 +02:00
return self . HandleFocus ( )
2019-11-16 05:00:27 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleNextPage ( ) error {
2021-11-01 00:35:54 +02:00
view , err := self . Gui . g . View ( self . ViewName )
2020-03-28 04:44:20 +02:00
if err != nil {
return nil
}
2021-11-01 00:35:54 +02:00
delta := self . Gui . pageDelta ( view )
2020-10-01 23:56:14 +02:00
2021-11-01 00:35:54 +02:00
return self . handleLineChange ( delta )
2020-03-28 04:44:20 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleGotoTop ( ) error {
2021-11-01 00:35:54 +02:00
return self . handleLineChange ( - self . GetItemsLength ( ) )
2020-03-28 04:44:20 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleGotoBottom ( ) error {
2021-11-01 00:35:54 +02:00
return self . handleLineChange ( self . GetItemsLength ( ) )
2020-03-28 04:44:20 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandlePrevPage ( ) error {
2021-11-01 00:35:54 +02:00
view , err := self . Gui . g . View ( self . ViewName )
2020-03-28 04:44:20 +02:00
if err != nil {
return nil
}
2020-10-01 23:56:14 +02:00
2021-11-01 00:35:54 +02:00
delta := self . Gui . pageDelta ( view )
2020-10-01 23:56:14 +02:00
2021-11-01 00:35:54 +02:00
return self . handleLineChange ( - delta )
2020-03-28 04:44:20 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) HandleClick ( onClick func ( ) error ) error {
2021-11-02 11:35:53 +02:00
if self . ignoreKeybinding ( ) {
2019-11-17 08:23:06 +02:00
return nil
}
2021-11-01 00:35:54 +02:00
view , err := self . Gui . g . View ( self . ViewName )
2021-04-02 10:20:40 +02:00
if err != nil {
return nil
}
2021-11-01 00:35:54 +02:00
prevSelectedLineIdx := self . GetPanelState ( ) . GetSelectedLineIdx ( )
2021-04-02 10:20:40 +02:00
newSelectedLineIdx := view . SelectedLineIdx ( )
2019-11-17 08:23:06 +02:00
2020-08-16 01:18:57 +02:00
// we need to focus the view
2022-01-16 05:46:53 +02:00
if err := self . Gui . c . PushContext ( self ) ; err != nil {
2020-08-16 01:18:57 +02:00
return err
}
2021-11-01 00:35:54 +02:00
if newSelectedLineIdx > self . GetItemsLength ( ) - 1 {
2020-08-16 05:58:29 +02:00
return nil
2019-11-17 08:23:06 +02:00
}
2021-11-01 00:35:54 +02:00
self . GetPanelState ( ) . SetSelectedLineIdx ( newSelectedLineIdx )
2019-11-17 08:23:06 +02:00
2021-11-01 00:35:54 +02:00
prevViewName := self . Gui . currentViewName ( )
2022-01-16 05:46:53 +02:00
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == self . ViewName && onClick != nil {
return onClick ( )
2019-11-17 08:23:06 +02:00
}
2021-11-01 00:35:54 +02:00
return self . HandleFocus ( )
2020-08-16 01:18:57 +02:00
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) OnSearchSelect ( selectedLineIdx int ) error {
2021-11-01 00:35:54 +02:00
self . GetPanelState ( ) . SetSelectedLineIdx ( selectedLineIdx )
return self . HandleFocus ( )
2020-08-16 01:18:57 +02:00
}
2021-11-21 03:48:49 +02:00
func ( self * ListContext ) HandleRenderToMain ( ) error {
if self . OnRenderToMain != nil {
return self . OnRenderToMain ( )
}
return nil
}
2022-01-16 05:46:53 +02:00
func ( self * ListContext ) Keybindings (
getKey func ( key string ) interface { } ,
config config . KeybindingConfig ,
guards types . KeybindingGuards ,
) [ ] * types . Binding {
return [ ] * types . Binding {
{ Tag : "navigation" , Key : getKey ( config . Universal . PrevItemAlt ) , Modifier : gocui . ModNone , Handler : self . HandlePrevLine } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . PrevItem ) , Modifier : gocui . ModNone , Handler : self . HandlePrevLine } ,
{ Tag : "navigation" , Key : gocui . MouseWheelUp , Modifier : gocui . ModNone , Handler : self . HandlePrevLine } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . NextItemAlt ) , Modifier : gocui . ModNone , Handler : self . HandleNextLine } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . NextItem ) , Modifier : gocui . ModNone , Handler : self . HandleNextLine } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . PrevPage ) , Modifier : gocui . ModNone , Handler : self . HandlePrevPage , Description : self . Gui . c . Tr . LcPrevPage } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . NextPage ) , Modifier : gocui . ModNone , Handler : self . HandleNextPage , Description : self . Gui . c . Tr . LcNextPage } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . GotoTop ) , Modifier : gocui . ModNone , Handler : self . HandleGotoTop , Description : self . Gui . c . Tr . LcGotoTop } ,
{ Key : gocui . MouseLeft , Modifier : gocui . ModNone , Handler : func ( ) error { return self . HandleClick ( nil ) } } ,
{ Tag : "navigation" , Key : gocui . MouseWheelDown , Modifier : gocui . ModNone , Handler : self . HandleNextLine } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . ScrollLeft ) , Modifier : gocui . ModNone , Handler : self . HandleScrollLeft } ,
{ Tag : "navigation" , Key : getKey ( config . Universal . ScrollRight ) , Modifier : gocui . ModNone , Handler : self . HandleScrollRight } ,
{
Key : getKey ( config . Universal . StartSearch ) ,
Handler : func ( ) error { return self . Gui . handleOpenSearch ( self . GetViewName ( ) ) } ,
Description : self . Gui . c . Tr . LcStartSearch ,
Tag : "navigation" ,
} ,
{
Key : getKey ( config . Universal . GotoBottom ) ,
Description : self . Gui . c . Tr . LcGotoBottom ,
2022-01-23 05:40:28 +02:00
Handler : self . HandleGotoBottom ,
2022-01-16 05:46:53 +02:00
Tag : "navigation" ,
} ,
}
}