From 901d2e323668d97fe3aa2d1e679352c7f53f4af6 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 10 May 2020 16:59:02 +0200 Subject: [PATCH] LibVT: Make selection follow terminal history scrollback :^) The buffer positions referred to by a VT::Position now include history scrollback, meaning that a VT::Position with row=0 is at the start of the history. The active terminal buffer keeps moving in VT::Position coordinates whenever we scroll. This allows selection to follow history. It also allows us to click hyperlinks in history. Fixes #957. --- Libraries/LibVT/Terminal.cpp | 10 +++++----- Libraries/LibVT/Terminal.h | 17 ++++++++++++----- Libraries/LibVT/TerminalWidget.cpp | 25 ++++++++++--------------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/Libraries/LibVT/Terminal.cpp b/Libraries/LibVT/Terminal.cpp index 4770c98032..608f6016b9 100644 --- a/Libraries/LibVT/Terminal.cpp +++ b/Libraries/LibVT/Terminal.cpp @@ -107,7 +107,7 @@ bool Terminal::Line::has_only_one_background_color() const void Terminal::clear() { for (size_t i = 0; i < rows(); ++i) - line(i).clear(m_current_attribute); + m_lines[i].clear(m_current_attribute); set_cursor(0, 0); } @@ -587,7 +587,7 @@ void Terminal::escape$P(const ParamVector& params) if (num == 0) num = 1; - auto& line = this->line(m_cursor_row); + auto& line = m_lines[m_cursor_row]; // Move n characters of line to the left for (int i = m_cursor_column; i < line.m_length - num; i++) @@ -827,7 +827,7 @@ void Terminal::put_character_at(unsigned row, unsigned column, u8 ch) { ASSERT(row < rows()); ASSERT(column < columns()); - auto& line = this->line(row); + auto& line = m_lines[row]; line.characters[column] = ch; line.attributes[column] = m_current_attribute; line.attributes[column].flags |= Attribute::Touched; @@ -1087,7 +1087,7 @@ void Terminal::set_size(u16 columns, u16 rows) void Terminal::invalidate_cursor() { - line(m_cursor_row).dirty = true; + m_lines[m_cursor_row].dirty = true; } void Terminal::execute_hashtag(u8 hashtag) @@ -1110,7 +1110,7 @@ Attribute Terminal::attribute_at(const Position& position) const { if (!position.is_valid()) return {}; - if (position.row() >= static_cast(m_lines.size())) + if (position.row() >= static_cast(line_count())) return {}; auto& line = this->line(position.row()); if (position.column() >= line.m_length) diff --git a/Libraries/LibVT/Terminal.h b/Libraries/LibVT/Terminal.h index 85d148e4bc..e3946e2fe3 100644 --- a/Libraries/LibVT/Terminal.h +++ b/Libraries/LibVT/Terminal.h @@ -37,7 +37,7 @@ namespace VT { class TerminalClient { public: - virtual ~TerminalClient() { } + virtual ~TerminalClient() {} virtual void beep() = 0; virtual void set_window_title(const StringView&) = 0; @@ -126,15 +126,22 @@ public: u16 m_length { 0 }; }; + size_t line_count() const + { + return m_history.size() + m_lines.size(); + } + Line& line(size_t index) { - ASSERT(index < m_rows); - return m_lines[index]; + if (index < m_history.size()) + return m_history[index]; + return m_lines[index - m_history.size()]; } const Line& line(size_t index) const { - ASSERT(index < m_rows); - return m_lines[index]; + if (index < m_history.size()) + return m_history[index]; + return m_lines[index - m_history.size()]; } size_t max_history_size() const { return 500; } diff --git a/Libraries/LibVT/TerminalWidget.cpp b/Libraries/LibVT/TerminalWidget.cpp index 24a72cc1f0..feb9ace106 100644 --- a/Libraries/LibVT/TerminalWidget.cpp +++ b/Libraries/LibVT/TerminalWidget.cpp @@ -327,7 +327,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) invalidate_cursor(); int rows_from_history = 0; - int first_row_from_history = 0; + int first_row_from_history = m_terminal.history().size(); int row_with_cursor = m_terminal.cursor_row(); if (m_scrollbar->value() != m_scrollbar->max()) { rows_from_history = min((int)m_terminal.rows(), m_scrollbar->max() - m_scrollbar->value()); @@ -335,17 +335,11 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) row_with_cursor = m_terminal.cursor_row() + rows_from_history; } - auto line_for_visual_row = [&](u16 row) -> const VT::Terminal::Line& { - if (row < rows_from_history) - return m_terminal.history().at(first_row_from_history + row); - return m_terminal.line(row - rows_from_history); - }; - - for (u16 row = 0; row < m_terminal.rows(); ++row) { - auto row_rect = this->row_rect(row); + for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) { + auto row_rect = this->row_rect(visual_row); if (!event.rect().contains(row_rect)) continue; - auto& line = line_for_visual_row(row); + auto& line = m_terminal.line(first_row_from_history + visual_row); bool has_only_one_background_color = line.has_only_one_background_color(); if (m_visual_beep_timer->is_active()) painter.clear_rect(row_rect, Color::Red); @@ -380,12 +374,12 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) for (u16 column = this_char_column; column < next_char_column; ++column) { should_reverse_fill_for_cursor_or_selection |= m_cursor_blink_state && m_has_logical_focus - && row == row_with_cursor + && visual_row == row_with_cursor && column == m_terminal.cursor_column(); - should_reverse_fill_for_cursor_or_selection |= selection_contains({ row, column }); + should_reverse_fill_for_cursor_or_selection |= selection_contains({ first_row_from_history + visual_row, column }); attribute = line.attributes[column]; text_color = color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.background_color : attribute.foreground_color); - auto character_rect = glyph_rect(row, column); + auto character_rect = glyph_rect(visual_row, column); auto cell_rect = character_rect.inflated(0, m_line_spacing); if (!has_only_one_background_color || should_reverse_fill_for_cursor_or_selection) { painter.clear_rect(cell_rect, color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.foreground_color : attribute.background_color).with_alpha(m_opacity)); @@ -429,7 +423,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) if (codepoint == ' ') continue; - auto character_rect = glyph_rect(row, this_char_column); + auto character_rect = glyph_rect(visual_row, this_char_column); auto num_columns = next_char_column - this_char_column; character_rect.move_by((num_columns - 1) * font().glyph_width('x') / 2, 0); painter.draw_glyph_or_emoji( @@ -441,7 +435,7 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) } if (!m_has_logical_focus && row_with_cursor < m_terminal.rows()) { - auto& cursor_line = line_for_visual_row(row_with_cursor); + auto& cursor_line = m_terminal.line(first_row_from_history + row_with_cursor); if (m_terminal.cursor_row() < (m_terminal.rows() - rows_from_history)) { auto cell_rect = glyph_rect(row_with_cursor, m_terminal.cursor_column()).inflated(0, m_line_spacing); painter.draw_rect(cell_rect, color_from_rgb(cursor_line.attributes[m_terminal.cursor_column()].foreground_color)); @@ -586,6 +580,7 @@ VT::Position TerminalWidget::buffer_position_at(const Gfx::Point& position) cons row = m_terminal.rows() - 1; if (column >= m_terminal.columns()) column = m_terminal.columns() - 1; + row += m_scrollbar->value(); return { row, column }; }