diff --git a/docs/changelog.rst b/docs/changelog.rst index bcd9e4d30..4bdb94a6f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,10 @@ Changelog 0.12.0 [future] ------------------------------ +- Preserve the mouse selection even when the contents of the screen are + scrolled or overwritten provided the new text does not intersect the + selected lines. + - Linux: Implement support for Input Method Extensions (multilingual input using standard keyboards) via `IBus `_ (:iss:`469`) diff --git a/kitty/screen.c b/kitty/screen.c index 3fa076606..76ad395d9 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -146,6 +146,7 @@ screen_reset(Screen *self) { init_tabstops(self->alt_tabstops, self->columns); cursor_reset(self->cursor); self->is_dirty = true; + self->selection = EMPTY_SELECTION; screen_cursor_position(self, 1, 1); set_dynamic_color(self, 110, NULL); set_dynamic_color(self, 111, NULL); @@ -332,6 +333,14 @@ move_widened_char(Screen *self, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type *dest_gpu = src_gpu; } +static inline bool +selection_has_screen_line(Selection *s, int y) { + if (s->start_scrolled_by == s->end_scrolled_by && s->start_x == s->end_x && s->start_y == s->end_y) return false; + int top = (int)s->start_y - s->start_scrolled_by; + int bottom = (int)s->end_y - s->end_scrolled_by; + return top <= y && y <= bottom; +} + static inline void draw_combining_char(Screen *self, char_type ch) { bool has_prev_char = false; @@ -350,6 +359,7 @@ draw_combining_char(Screen *self, char_type ch) { if (has_prev_char) { line_add_combining_char(self->linebuf->line, ch, xpos); self->is_dirty = true; + if (selection_has_screen_line(&self->selection, ypos)) self->selection = EMPTY_SELECTION; linebuf_mark_line_dirty(self->linebuf, ypos); if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji CPUCell *cpu_cell = self->linebuf->line->cpu_cells + xpos; @@ -398,6 +408,7 @@ screen_draw(Screen *self, uint32_t och) { self->cursor->x++; } self->is_dirty = true; + if (selection_has_screen_line(&self->selection, self->cursor->y)) self->selection = EMPTY_SELECTION; linebuf_mark_line_dirty(self->linebuf, self->cursor->y); } @@ -563,6 +574,7 @@ screen_toggle_screen_buffer(Screen *self) { } screen_history_scroll(self, SCROLL_FULL, false); self->is_dirty = true; + self->selection = EMPTY_SELECTION; } void screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI @@ -782,6 +794,22 @@ screen_cursor_to_column(Screen *self, unsigned int column) { } } +static inline void +index_selection(Screen *self, Selection *s, bool up) { + if (s->start_scrolled_by == s->end_scrolled_by && s->start_x == s->end_x && s->start_y == s->end_y) return; + if (up) { + if (s->start_y == 0) s->start_scrolled_by += 1; + else s->start_y--; + if (s->end_y == 0) s->end_scrolled_by += 1; + else s->end_y--; + } else { + if (s->start_y >= self->lines - 1) s->start_scrolled_by -= 1; + else s->start_y++; + if (s->end_y >= self->lines - 1) s->end_scrolled_by -= 1; + else s->end_y++; + } +} + #define INDEX_GRAPHICS(amtv) { \ bool is_main = self->linebuf == self->main_linebuf; \ static ScrollData s; \ @@ -802,7 +830,8 @@ screen_cursor_to_column(Screen *self, unsigned int column) { self->history_line_added_count++; \ } \ linebuf_clear_line(self->linebuf, bottom); \ - self->is_dirty = true; + self->is_dirty = true; \ + index_selection(self, &self->selection, true); void screen_index(Screen *self) { @@ -828,7 +857,8 @@ screen_scroll(Screen *self, unsigned int count) { linebuf_reverse_index(self->linebuf, top, bottom); \ linebuf_clear_line(self->linebuf, top); \ INDEX_GRAPHICS(1) \ - self->is_dirty = true; + self->is_dirty = true; \ + index_selection(self, &self->selection, false); void screen_reverse_index(Screen *self) { @@ -1012,6 +1042,7 @@ screen_erase_in_line(Screen *self, unsigned int how, bool private) { line_apply_cursor(self->linebuf->line, self->cursor, s, n, true); } self->is_dirty = true; + if (selection_has_screen_line(&self->selection, self->cursor->y)) self->selection = EMPTY_SELECTION; linebuf_mark_line_dirty(self->linebuf, self->cursor->y); } } @@ -1055,6 +1086,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) { linebuf_mark_line_dirty(self->linebuf, i); } self->is_dirty = true; + self->selection = EMPTY_SELECTION; } if (how != 2) { screen_erase_in_line(self, how, private); @@ -1075,6 +1107,7 @@ screen_insert_lines(Screen *self, unsigned int count) { if (top <= self->cursor->y && self->cursor->y <= bottom) { linebuf_insert_lines(self->linebuf, count, self->cursor->y, bottom); self->is_dirty = true; + self->selection = EMPTY_SELECTION; screen_carriage_return(self); } } @@ -1086,6 +1119,7 @@ screen_delete_lines(Screen *self, unsigned int count) { if (top <= self->cursor->y && self->cursor->y <= bottom) { linebuf_delete_lines(self->linebuf, count, self->cursor->y, bottom); self->is_dirty = true; + self->selection = EMPTY_SELECTION; screen_carriage_return(self); } } @@ -1102,6 +1136,7 @@ screen_insert_characters(Screen *self, unsigned int count) { line_apply_cursor(self->linebuf->line, self->cursor, x, num, true); linebuf_mark_line_dirty(self->linebuf, self->cursor->y); self->is_dirty = true; + if (selection_has_screen_line(&self->selection, self->cursor->y)) self->selection = EMPTY_SELECTION; } } @@ -1118,6 +1153,7 @@ screen_delete_characters(Screen *self, unsigned int count) { line_apply_cursor(self->linebuf->line, self->cursor, self->columns - num, num, true); linebuf_mark_line_dirty(self->linebuf, self->cursor->y); self->is_dirty = true; + if (selection_has_screen_line(&self->selection, self->cursor->y)) self->selection = EMPTY_SELECTION; } } @@ -1131,6 +1167,7 @@ screen_erase_characters(Screen *self, unsigned int count) { line_apply_cursor(self->linebuf->line, self->cursor, x, num, true); linebuf_mark_line_dirty(self->linebuf, self->cursor->y); self->is_dirty = true; + if (selection_has_screen_line(&self->selection, self->cursor->y)) self->selection = EMPTY_SELECTION; } // }}} @@ -1423,7 +1460,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat update_line_data(self->linebuf->line, y, address); } if (was_dirty) { - self->selection = EMPTY_SELECTION; self->url_range = EMPTY_SELECTION; + self->url_range = EMPTY_SELECTION; } }