diff --git a/pkg/gocui/tcell_driver.go b/pkg/gocui/tcell_driver.go index 885bcbabb..e70fc3440 100644 --- a/pkg/gocui/tcell_driver.go +++ b/pkg/gocui/tcell_driver.go @@ -366,7 +366,9 @@ func gocuiEventFromTcellEvent(tev tcell.Event) GocuiEvent { // process button events (not wheel events) button &= tcell.ButtonMask(0xff) + newButtonPress := false if button != tcell.ButtonNone && lastMouseKey == tcell.ButtonNone { + newButtonPress = true lastMouseKey = button lastMouseMod = tev.Modifiers() switch button { @@ -410,9 +412,23 @@ func gocuiEventFromTcellEvent(tev tcell.Event) GocuiEvent { } // if we haven't released the left mouse button and we've moved the cursor then we're dragging case MAYBE_DRAGGING: - if x != lastX || y != lastY { - dragState = DRAGGING + if x == lastX && y == lastY { + // Deliver the button press itself, but swallow held-button + // motion events within the same cell: they carry no new + // information, and if they fell through they would be + // delivered with the default MouseRelease key. + if !newButtonPress { + return GocuiEvent{Type: eventNone} + } + break } + // The first movement is already part of the drag; give it the + // same key and modifier as the DRAGGING events below so it + // reaches drag bindings instead of being delivered with the + // default MouseRelease key. + dragState = DRAGGING + mouseMod = ModMotion + mouseKey = MouseLeft case DRAGGING: mouseMod = ModMotion mouseKey = MouseLeft diff --git a/pkg/gocui/tcell_driver_test.go b/pkg/gocui/tcell_driver_test.go new file mode 100644 index 000000000..be36efe11 --- /dev/null +++ b/pkg/gocui/tcell_driver_test.go @@ -0,0 +1,33 @@ +package gocui + +import ( + "testing" + + "github.com/gdamore/tcell/v3" + "github.com/stretchr/testify/assert" +) + +func TestFirstMouseMovementAfterPressIsDragEvent(t *testing.T) { + t.Cleanup(resetMouseState) + resetMouseState() + + pressEvent := gocuiEventFromTcellEvent(tcell.NewEventMouse(1, 2, tcell.ButtonPrimary, tcell.ModNone)) + unchangedHeldEvent := gocuiEventFromTcellEvent(tcell.NewEventMouse(1, 2, tcell.ButtonPrimary, tcell.ModNone)) + dragEvent := gocuiEventFromTcellEvent(tcell.NewEventMouse(1, 3, tcell.ButtonPrimary, tcell.ModNone)) + + assert.Equal(t, eventMouse, pressEvent.Type) + assert.Equal(t, MouseLeft, pressEvent.Key.KeyName()) + assert.Equal(t, ModNone, pressEvent.Key.Mod()) + assert.Equal(t, eventNone, unchangedHeldEvent.Type) + assert.Equal(t, eventMouse, dragEvent.Type) + assert.Equal(t, MouseLeft, dragEvent.Key.KeyName()) + assert.Equal(t, ModMotion, dragEvent.Key.Mod()) +} + +func resetMouseState() { + lastMouseKey = tcell.ButtonNone + lastMouseMod = tcell.ModNone + dragState = NOT_DRAGGING + lastX = 0 + lastY = 0 +}