diff --git a/docs/changelog.rst b/docs/changelog.rst index e2d60bafc..23837cbef 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -41,6 +41,9 @@ To update |kitty|, :doc:`follow the instructions `. - Fix the remote file kitten not working when using -- with ssh. The ssh kitten was recently changed to do this (:iss:`3929`) +- When dragging word or line selections, ensure the initially selected item is + never deselected. This matches behavior in most other programs (:iss:`3930`) + 0.22.2 [2021-08-02] ---------------------- diff --git a/kitty/screen.c b/kitty/screen.c index 01f6497a1..ff014d1b8 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -239,6 +239,8 @@ index_selection(const Screen *self, Selections *selections, bool up) { s->start.y--; if (s->input_start.y) s->input_start.y--; if (s->input_current.y) s->input_current.y--; + if (s->initial_extent.start.y) s->initial_extent.start.y--; + if (s->initial_extent.end.y) s->initial_extent.end.y--; } if (s->end.y == 0) s->end_scrolled_by += 1; else s->end.y--; @@ -2823,12 +2825,12 @@ screen_update_selection(Screen *self, index_type x, index_type y, bool in_left_h s->input_current.x = x; s->input_current.y = y; s->input_current.in_left_half_of_cell = in_left_half_of_cell; SelectionBoundary start, end, *a = &s->start, *b = &s->end, abs_start, abs_end, abs_current_input; -#define absy(b, scrolled_by) b.y = scrolled_by + self->lines - 1 - b.y - abs_start = s->start; absy(abs_start, s->start_scrolled_by); - abs_end = s->end; absy(abs_end, s->end_scrolled_by); - abs_current_input = s->input_current; absy(abs_current_input, self->scrolled_by); -#undef absy - if (upd.set_as_nearest_extend) { +#define set_abs(which, initializer, scrolled_by) which = initializer; which.y = scrolled_by + self->lines - 1 - which.y; + set_abs(abs_start, s->start, s->start_scrolled_by); + set_abs(abs_end, s->end, s->end_scrolled_by); + set_abs(abs_current_input, s->input_current, self->scrolled_by); + if (upd.set_as_nearest_extend || self->selections.extension_in_progress) { + self->selections.extension_in_progress = true; bool start_is_nearer = false; if (self->selections.extend_mode == EXTEND_LINE || self->selections.extend_mode == EXTEND_LINE_FROM_POINT) { if (abs_start.y == abs_end.y) { @@ -2839,7 +2841,25 @@ screen_update_selection(Screen *self, index_type x, index_type y, bool in_left_h } } else start_is_nearer = num_cells_between_selection_boundaries(self, &abs_start, &abs_current_input) < num_cells_between_selection_boundaries(self, &abs_end, &abs_current_input); if (start_is_nearer) s->adjusting_start = true; + } else if (!upd.start_extended_selection && self->selections.extend_mode != EXTEND_CELL) { + SelectionBoundary abs_initial_start, abs_initial_end; + set_abs(abs_initial_start, s->initial_extent.start, s->initial_extent.scrolled_by); + set_abs(abs_initial_end, s->initial_extent.end, s->initial_extent.scrolled_by); + if (self->selections.extend_mode == EXTEND_WORD) { + s->adjusting_start = selection_boundary_less_than(&abs_current_input, &abs_initial_end); + } else { + const unsigned int initial_line = abs_initial_start.y; + if (initial_line == abs_current_input.y) { + s->adjusting_start = false; + s->start = s->initial_extent.start; s->start_scrolled_by = s->initial_extent.scrolled_by; + s->end = s->initial_extent.end; s->end_scrolled_by = s->initial_extent.scrolled_by; + } + else { + s->adjusting_start = abs_current_input.y > initial_line; + } + } } +#undef set_abs bool adjusted_boundary_is_before; if (s->adjusting_start) adjusted_boundary_is_before = selection_boundary_less_than(&abs_start, &abs_end); else { adjusted_boundary_is_before = selection_boundary_less_than(&abs_end, &abs_start); } @@ -2919,7 +2939,13 @@ screen_update_selection(Screen *self, index_type x, index_type y, bool in_left_h } if (!self->selections.in_progress) { s->adjusting_start = false; + self->selections.extension_in_progress = false; call_boss(set_primary_selection, NULL); + } else { + if (upd.start_extended_selection && self->selections.extend_mode != EXTEND_CELL) { + s->initial_extent.start = s->start; s->initial_extent.end = s->end; + s->initial_extent.scrolled_by = s->start_scrolled_by; + } } } diff --git a/kitty/screen.h b/kitty/screen.h index c1c089065..f39130a88 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -42,12 +42,16 @@ typedef struct { bool rectangle_select, adjusting_start; IterationData last_rendered; int sort_y, sort_x; + struct { + SelectionBoundary start, end; + unsigned int scrolled_by; + } initial_extent; } Selection; typedef struct { Selection *items; size_t count, capacity, last_rendered_count; - bool in_progress; + bool in_progress, extension_in_progress; SelectionExtendMode extend_mode; } Selections; diff --git a/kitty_tests/mouse.py b/kitty_tests/mouse.py index 942a764d6..12363d481 100644 --- a/kitty_tests/mouse.py +++ b/kitty_tests/mouse.py @@ -150,6 +150,11 @@ class TestMouse(BaseTest): multi_click(x=1, y=2) self.ae(sel(), 'stuvX') release() + multi_click(x=3.6) + self.ae(sel(), 'cd') + move(0.2) + release() + self.ae(sel(), 'ab cd') # Line select with drag s.reset() @@ -169,6 +174,15 @@ class TestMouse(BaseTest): move() self.ae(sel(), str(s.line(0))) release() + multi_click(y=1, count=3) + self.ae(sel(), '4 5 6') + move(y=0) + self.ae(sel(), '1 2 3\n4 5 6') + move(y=1) + self.ae(sel(), '4 5 6') + move(y=2) + self.ae(sel(), '4 5 6\n7 8 9X') + release() s.reset() s.draw(' 123') s.linefeed(), s.carriage_return() @@ -183,8 +197,8 @@ class TestMouse(BaseTest): release(x=2, y=1, button=GLFW_MOUSE_BUTTON_RIGHT) self.ae(sel(), '123\n 456') press(button=GLFW_MOUSE_BUTTON_RIGHT) - release(button=GLFW_MOUSE_BUTTON_RIGHT) self.ae(sel(), ' 123\n 456') + release(button=GLFW_MOUSE_BUTTON_RIGHT) # Rectangle select init()