diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index e314c0f3f..8522c39bf 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -29,8 +29,9 @@ type LocalCommitsController struct { *ListControllerTrait[*models.Commit] c *ControllerCommon - pullFiles PullFilesFn - commitDrag *commitDragState + pullFiles PullFilesFn + commitDrag *commitDragState + dragAutoscroller *helpers.DragAutoscroller } // commitDragState tracks a mouse drag that moves the selected commits. It is @@ -77,7 +78,7 @@ func NewLocalCommitsController( c *ControllerCommon, pullFiles PullFilesFn, ) *LocalCommitsController { - return &LocalCommitsController{ + controller := &LocalCommitsController{ baseController: baseController{}, c: c, pullFiles: pullFiles, @@ -88,6 +89,13 @@ func NewLocalCommitsController( c.Contexts().LocalCommits.GetSelectedItems, ), } + controller.dragAutoscroller = helpers.NewDragAutoscroller( + c.HelperCommon, + c.Contexts().LocalCommits, + controller.canCommitDragAutoscroll, + controller.handleCommitDragAutoscroll, + ) + return controller } func (self *LocalCommitsController) GetMouseKeybindings(types.KeybindingsOpts) []*gocui.ViewMouseBinding { @@ -176,13 +184,22 @@ func (self *LocalCommitsController) handleCommitDrag(opts gocui.ViewMouseBinding } self.commitDrag.hasMoved = true + if self.updateCommitDragInsertion(opts.Y) { + self.c.PostRefreshUpdate(self.context()) + } + originY := self.context().GetView().OriginY() + self.dragAutoscroller.Update(opts.Y - originY) self.restoreCommitDragHighlight() - insertionIndex := self.commitDragInsertionIndex(opts.Y) + return nil +} + +func (self *LocalCommitsController) updateCommitDragInsertion(viewIndex int) bool { + insertionIndex := self.commitDragInsertionIndex(viewIndex) if insertionIndex >= self.commitDrag.startIndex && insertionIndex <= self.commitDrag.endIndex+1 { insertionIndex = -1 } if insertionIndex == self.commitDrag.insertionIndex { - return nil + return false } self.commitDrag.insertionIndex = insertionIndex @@ -191,8 +208,7 @@ func (self *LocalCommitsController) handleCommitDrag(opts gocui.ViewMouseBinding } else { self.context().SetDropInsertionIndex(insertionIndex) } - self.c.PostRefreshUpdate(self.context()) - return nil + return true } // gocui moves the view cursor to the pointer position before invoking our @@ -235,6 +251,7 @@ func (self *LocalCommitsController) handleCommitDragRelease(gocui.ViewMouseBindi } state := self.commitDrag + self.dragAutoscroller.Cancel() self.commitDrag = nil self.context().ClearDropInsertionIndex() @@ -312,6 +329,7 @@ func (self *LocalCommitsController) GetOnFocusLost() func(types.OnFocusLostOpts) return } + self.dragAutoscroller.Cancel() self.commitDrag = nil self.c.GocuiGui().CancelMouseCapture() self.context().ClearDropInsertionIndex() @@ -319,6 +337,32 @@ func (self *LocalCommitsController) GetOnFocusLost() func(types.OnFocusLostOpts) } } +// Stop autoscrolling once the insertion point has reached the end of the +// allowed range in the scroll direction; e.g. during a rebase there is no +// point in scrolling on into the section of real commits. +func (self *LocalCommitsController) canCommitDragAutoscroll(direction int) bool { + state := self.commitDrag + if state == nil { + return false + } + if direction < 0 { + return state.insertionIndex != state.minInsertion + } + return state.insertionIndex != state.maxInsertion +} + +func (self *LocalCommitsController) handleCommitDragAutoscroll(viewIndex int) bool { + if self.commitDrag == nil { + return false + } + + self.updateCommitDragInsertion(viewIndex) + self.context().SetNeedRerenderVisibleLines() + self.context().HandleRender() + self.restoreCommitDragHighlight() + return self.canCommitDragAutoscroll(self.dragAutoscroller.Direction()) +} + func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { editCommitKey := opts.Config.Universal.Edit diff --git a/pkg/integration/tests/interactive_rebase/drag_to_reorder_with_autoscroll.go b/pkg/integration/tests/interactive_rebase/drag_to_reorder_with_autoscroll.go new file mode 100644 index 000000000..d8086e0a3 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/drag_to_reorder_with_autoscroll.go @@ -0,0 +1,35 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DragToReorderWithAutoscroll = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Keep scrolling commits while a dragged commit is held at the panel edge", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateNCommits(40) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + TopLines( + Contains("commit-40").IsSelected(), + ). + ClickAndHold(1, 0). + MouseMoveToBottom(1). + OriginYAtLeast(3). + MouseRelease(). + SelectedLines( + Contains("commit-40"), + ). + SelectedLineIdxAtLeast(3). + GotoTop(). + TopLines( + Contains("commit-39").IsSelected(), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index fdc375dca..fe8478b9c 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -293,6 +293,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.DragKeepsSelectionHighlighted, interactive_rebase.DragToReorder, interactive_rebase.DragToReorderInRebase, + interactive_rebase.DragToReorderWithAutoscroll, interactive_rebase.DropCommitInCopiedBranchWithUpdateRef, interactive_rebase.DropMergeCommit, interactive_rebase.DropTodoCommitWithUpdateRef,