diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 0a02e7398..9407e3dee 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -191,7 +191,7 @@ func (self *LocalCommitsController) handleCommitDrag(opts gocui.ViewMouseBinding self.commitDrag.hasMoved = true if self.updateCommitDragInsertion(opts.Y) { - self.c.PostRefreshUpdate(self.context()) + self.c.PostRefreshUpdateKeepingScrollPosition(self.context()) } originY := self.context().GetView().OriginY() self.dragAutoscroller.Update(opts.Y - originY) diff --git a/pkg/integration/components/test_test.go b/pkg/integration/components/test_test.go index bda63e3fb..2495a07c5 100644 --- a/pkg/integration/components/test_test.go +++ b/pkg/integration/components/test_test.go @@ -28,6 +28,9 @@ type fakeGuiDriver struct { heldCoordinates []coordinate movedCoordinates []coordinate releasedCoordinates []coordinate + scrolledCoordinates []coordinate + onUIThread bool + onUIThreadCallCount int } var _ integrationTypes.GuiDriver = &fakeGuiDriver{} @@ -57,13 +60,17 @@ func (self *fakeGuiDriver) MouseRelease(x, y int) { } func (self *fakeGuiDriver) ScrollWheelDown(x, y int) { + self.scrolledCoordinates = append(self.scrolledCoordinates, coordinate{x: x, y: y}) } func (self *fakeGuiDriver) RefreshInBackground() { } func (self *fakeGuiDriver) OnUIThreadAndWait(f func()) { + self.onUIThreadCallCount++ + self.onUIThread = true f() + self.onUIThread = false } func (self *fakeGuiDriver) FocusIn() { @@ -167,6 +174,42 @@ func TestSuccess(t *testing.T) { assert.Equal(t, "", driver.failureMessage) } +func TestViewDriverPointerCoordinates(t *testing.T) { + guiDriver := &fakeGuiDriver{} + testDriver := NewTestDriver(guiDriver, nil, config.KeybindingConfig{}, 0) + view := gocui.NewView("source", 10, 20, 30, 31, gocui.OutputNormal) + targetView := gocui.NewView("target", 40, 50, 60, 61, gocui.OutputNormal) + viewDriver := &ViewDriver{ + getView: func() *gocui.View { + assert.True(t, guiDriver.onUIThread) + return view + }, + t: testDriver, + } + targetViewDriver := &ViewDriver{ + getView: func() *gocui.View { + assert.True(t, guiDriver.onUIThread) + return targetView + }, + t: testDriver, + } + + viewDriver. + Click(1, 2). + FocusInAndClick(3, 4). + ClickAndHold(5, 6). + MouseMove(7, 8). + MouseMoveToBottom(9). + MouseMoveToView(targetViewDriver, 10, 11). + ScrollWheelDown() + + assert.Equal(t, []coordinate{{12, 23}, {14, 25}}, guiDriver.clickedCoordinates) + assert.Equal(t, []coordinate{{16, 27}}, guiDriver.heldCoordinates) + assert.Equal(t, []coordinate{{18, 29}, {20, 30}, {51, 62}}, guiDriver.movedCoordinates) + assert.Equal(t, []coordinate{{11, 21}}, guiDriver.scrolledCoordinates) + assert.Equal(t, 7, guiDriver.onUIThreadCallCount) +} + func TestFailingFixture(t *testing.T) { test := NewIntegrationTest(NewIntegrationTestArgs{ Description: unitTestDescription, diff --git a/pkg/integration/components/view_driver.go b/pkg/integration/components/view_driver.go index 23e3502a1..b743afde7 100644 --- a/pkg/integration/components/view_driver.go +++ b/pkg/integration/components/view_driver.go @@ -517,7 +517,7 @@ func (self *ViewDriver) PressRapidly(keys ...config.Keybinding) *ViewDriver { } func (self *ViewDriver) Click(x, y int) *ViewDriver { - offsetX, offsetY, _, _ := self.getView().Dimensions() + offsetX, offsetY, _ := self.viewGeometry() self.t.click(offsetX+1+x, offsetY+1+y) @@ -525,7 +525,7 @@ func (self *ViewDriver) Click(x, y int) *ViewDriver { } func (self *ViewDriver) FocusInAndClick(x, y int) *ViewDriver { - offsetX, offsetY, _, _ := self.getView().Dimensions() + offsetX, offsetY, _ := self.viewGeometry() self.t.focusInAndClick(offsetX+1+x, offsetY+1+y) @@ -533,7 +533,7 @@ func (self *ViewDriver) FocusInAndClick(x, y int) *ViewDriver { } func (self *ViewDriver) MouseMoveToView(target *ViewDriver, x, y int) *ViewDriver { - offsetX, offsetY, _, _ := target.getView().Dimensions() + offsetX, offsetY, _ := target.viewGeometry() self.t.mouseMove(offsetX+1+x, offsetY+1+y) return self } @@ -543,30 +543,42 @@ func (self *ViewDriver) Drag(fromX, fromY, toX, toY int) *ViewDriver { } func (self *ViewDriver) ClickAndHold(x, y int) *ViewDriver { - offsetX, offsetY, _, _ := self.getView().Dimensions() + offsetX, offsetY, _ := self.viewGeometry() self.t.clickAndHold(offsetX+1+x, offsetY+1+y) return self } func (self *ViewDriver) MouseMove(x, y int) *ViewDriver { - offsetX, offsetY, _, _ := self.getView().Dimensions() + offsetX, offsetY, _ := self.viewGeometry() self.t.mouseMove(offsetX+1+x, offsetY+1+y) return self } func (self *ViewDriver) MouseMoveToBottom(x int) *ViewDriver { - return self.MouseMove(x, self.getView().InnerHeight()-1) + offsetX, offsetY, innerHeight := self.viewGeometry() + self.t.mouseMove(offsetX+1+x, offsetY+innerHeight) + return self } // scrolls the view down by one notch of the mouse wheel, i.e. by // gui.scrollHeight lines. This moves the scroll position without moving the // selection. func (self *ViewDriver) ScrollWheelDown() *ViewDriver { - offsetX, offsetY, _, _ := self.getView().Dimensions() + offsetX, offsetY, _ := self.viewGeometry() self.t.scrollWheelDown(offsetX+1, offsetY+1) return self } +func (self *ViewDriver) viewGeometry() (offsetX int, offsetY int, innerHeight int) { + self.t.gui.OnUIThreadAndWait(func() { + view := self.getView() + offsetX, offsetY, _, _ = view.Dimensions() + innerHeight = view.InnerHeight() + }) + + return offsetX, offsetY, innerHeight +} + func (self *ViewDriver) RepeatMouseMove() *ViewDriver { self.t.repeatMouseMove() return self 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 index d8086e0a3..fdfdb8bc8 100644 --- a/pkg/integration/tests/interactive_rebase/drag_to_reorder_with_autoscroll.go +++ b/pkg/integration/tests/interactive_rebase/drag_to_reorder_with_autoscroll.go @@ -19,14 +19,22 @@ var DragToReorderWithAutoscroll = NewIntegrationTest(NewIntegrationTestArgs{ TopLines( Contains("commit-40").IsSelected(), ). + // Click and hold the first commit ClickAndHold(1, 0). + // Move the mouse to the bottom of the panel to trigger autoscroll MouseMoveToBottom(1). + // Verify that the view scrolls + OriginYAtLeast(3). + // Move the mouse back into the viewport + MouseMove(1, 1). + // This keeps the scroll as it was OriginYAtLeast(3). MouseRelease(). SelectedLines( Contains("commit-40"), ). SelectedLineIdxAtLeast(3). + // Scroll back to verify that the original commit is no longer at the top GotoTop(). TopLines( Contains("commit-39").IsSelected(),