diff --git a/Libraries/LibVT/Line.cpp b/Libraries/LibVT/Line.cpp index 1760e2b901..39dc553704 100644 --- a/Libraries/LibVT/Line.cpp +++ b/Libraries/LibVT/Line.cpp @@ -36,7 +36,7 @@ Line::Line(u16 length) Line::~Line() { - delete[] m_characters; + delete[] m_codepoints; delete[] m_attributes; } @@ -44,17 +44,19 @@ void Line::set_length(u16 new_length) { if (m_length == new_length) return; - auto* new_characters = new u8[new_length]; + auto* new_codepoints = new u32[new_length]; auto* new_attributes = new Attribute[new_length]; - memset(new_characters, ' ', new_length); - if (m_characters && m_attributes) { - memcpy(new_characters, m_characters, min(m_length, new_length)); - for (size_t i = 0; i < min(m_length, new_length); ++i) + for (size_t i = 0; i < new_length; ++i) + new_codepoints[i] = ' '; + if (m_codepoints && m_attributes) { + for (size_t i = 0; i < min(m_length, new_length); ++i) { + new_codepoints[i] = m_codepoints[i]; new_attributes[i] = m_attributes[i]; + } } - delete[] m_characters; + delete[] m_codepoints; delete[] m_attributes; - m_characters = new_characters; + m_codepoints = new_codepoints; m_attributes = new_attributes; m_length = new_length; } @@ -62,15 +64,16 @@ void Line::set_length(u16 new_length) void Line::clear(Attribute attribute) { if (m_dirty) { - memset(m_characters, ' ', m_length); - for (u16 i = 0; i < m_length; ++i) + for (u16 i = 0; i < m_length; ++i) { + m_codepoints[i] = ' '; m_attributes[i] = attribute; + } return; } for (unsigned i = 0; i < m_length; ++i) { - if (m_characters[i] != ' ') + if (m_codepoints[i] != ' ') m_dirty = true; - m_characters[i] = ' '; + m_codepoints[i] = ' '; } for (unsigned i = 0; i < m_length; ++i) { if (m_attributes[i] != attribute) diff --git a/Libraries/LibVT/Line.h b/Libraries/LibVT/Line.h index 69e7762e1d..55034a9a50 100644 --- a/Libraries/LibVT/Line.h +++ b/Libraries/LibVT/Line.h @@ -88,12 +88,10 @@ public: bool has_only_one_background_color() const; void set_length(u16); - StringView text() const { return { m_characters, m_length }; } - u16 length() const { return m_length; } - const u8* characters() const { return m_characters; } - u8* characters() { return m_characters; } + const u32* codepoints() const { return m_codepoints; } + u32* codepoints() { return m_codepoints; } bool is_dirty() const { return m_dirty; } void set_dirty(bool b) { m_dirty = b; } @@ -102,7 +100,7 @@ public: Attribute* attributes() { return m_attributes; } private: - u8* m_characters { nullptr }; + u32* m_codepoints { nullptr }; Attribute* m_attributes { nullptr }; bool m_dirty { false }; u16 m_length { 0 }; diff --git a/Libraries/LibVT/Terminal.cpp b/Libraries/LibVT/Terminal.cpp index bd6b5f4274..ca0f08781e 100644 --- a/Libraries/LibVT/Terminal.cpp +++ b/Libraries/LibVT/Terminal.cpp @@ -353,7 +353,7 @@ void Terminal::escape$b(const ParamVector& params) return; for (unsigned i = 0; i < params[0]; ++i) - put_character_at(m_cursor_row, m_cursor_column++, m_last_char); + put_character_at(m_cursor_row, m_cursor_column++, m_last_codepoint); } void Terminal::escape$d(const ParamVector& params) @@ -529,11 +529,11 @@ void Terminal::escape$P(const ParamVector& params) // Move n characters of line to the left for (int i = m_cursor_column; i < line.length() - num; i++) - line.characters()[i] = line.characters()[i + num]; + line.codepoints()[i] = line.codepoints()[i + num]; // Fill remainder of line with blanks for (int i = line.length() - num; i < line.length(); i++) - line.characters()[i] = ' '; + line.codepoints()[i] = ' '; line.set_dirty(true); } @@ -761,17 +761,17 @@ void Terminal::set_cursor(unsigned a_row, unsigned a_column) invalidate_cursor(); } -void Terminal::put_character_at(unsigned row, unsigned column, u8 ch) +void Terminal::put_character_at(unsigned row, unsigned column, u32 ch) { ASSERT(row < rows()); ASSERT(column < columns()); auto& line = m_lines[row]; - line.characters()[column] = ch; + line.codepoints()[column] = ch; line.attributes()[column] = m_current_attribute; line.attributes()[column].flags |= Attribute::Touched; line.set_dirty(true); - m_last_char = ch; + m_last_codepoint = ch; } void Terminal::NEL() diff --git a/Libraries/LibVT/Terminal.h b/Libraries/LibVT/Terminal.h index 8e11041ac9..ce3c4201d3 100644 --- a/Libraries/LibVT/Terminal.h +++ b/Libraries/LibVT/Terminal.h @@ -105,7 +105,7 @@ private: void scroll_down(); void newline(); void set_cursor(unsigned row, unsigned column); - void put_character_at(unsigned row, unsigned column, u8 ch); + void put_character_at(unsigned row, unsigned column, u32 ch); void set_window_title(const String&); void unimplemented_escape(); @@ -188,8 +188,7 @@ private: Vector m_xterm_parameters; Vector m_horizontal_tabs; u8 m_final { 0 }; - - u8 m_last_char { 0 }; + u32 m_last_codepoint { 0 }; }; } diff --git a/Libraries/LibVT/TerminalWidget.cpp b/Libraries/LibVT/TerminalWidget.cpp index 5e8af42ccc..dd0af50c04 100644 --- a/Libraries/LibVT/TerminalWidget.cpp +++ b/Libraries/LibVT/TerminalWidget.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -338,86 +337,58 @@ void TerminalWidget::paint_event(GUI::PaintEvent& event) else if (has_only_one_background_color) painter.clear_rect(row_rect, color_from_rgb(line.attributes()[0].background_color).with_alpha(m_opacity)); - // The terminal insists on thinking characters and - // bytes are the same thing. We want to still draw - // emojis in *some* way, but it won't be completely - // perfect. So what we do is we make multi-byte - // characters take up multiple columns, and render - // the character itself in the center of the columns - // its bytes take up as far as the terminal is concerned. + for (size_t column = 0; column < line.length(); ++column) { + u32 codepoint = line.codepoints()[column]; + bool should_reverse_fill_for_cursor_or_selection = m_cursor_blink_state + && m_has_logical_focus + && visual_row == row_with_cursor + && column == m_terminal.cursor_column(); + should_reverse_fill_for_cursor_or_selection |= selection_contains({ first_row_from_history + visual_row, (int)column }); + auto attribute = line.attributes()[column]; + auto text_color = color_from_rgb(should_reverse_fill_for_cursor_or_selection ? attribute.background_color : attribute.foreground_color); + 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)); + } - Utf8View utf8_view { line.text() }; + enum class UnderlineStyle { + None, + Dotted, + Solid, + }; - for (auto it = utf8_view.begin(); it != utf8_view.end(); ++it) { - u32 codepoint = *it; - int this_char_column = utf8_view.byte_offset_of(it); - AK::Utf8CodepointIterator it_copy = it; - int next_char_column = utf8_view.byte_offset_of(++it_copy); + auto underline_style = UnderlineStyle::None; - // Columns from this_char_column up until next_char_column - // are logically taken up by this (possibly multi-byte) - // character. Iterate over these columns and draw background - // for each one of them separately. - - bool should_reverse_fill_for_cursor_or_selection = false; - VT::Attribute attribute; - Color text_color; - - 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 - && visual_row == row_with_cursor - && column == m_terminal.cursor_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(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)); - } - - enum class UnderlineStyle { - None, - Dotted, - Solid, - }; - - auto underline_style = UnderlineStyle::None; - - if (attribute.flags & VT::Attribute::Underline) { - // Content has specified underline + if (attribute.flags & VT::Attribute::Underline) { + // Content has specified underline + underline_style = UnderlineStyle::Solid; + } else if (!attribute.href.is_empty()) { + // We're hovering a hyperlink + if (m_hovered_href_id == attribute.href_id || m_active_href_id == attribute.href_id) underline_style = UnderlineStyle::Solid; - } else if (!attribute.href.is_empty()) { - // We're hovering a hyperlink - if (m_hovered_href_id == attribute.href_id || m_active_href_id == attribute.href_id) - underline_style = UnderlineStyle::Solid; - else - underline_style = UnderlineStyle::Dotted; - } + else + underline_style = UnderlineStyle::Dotted; + } - if (underline_style == UnderlineStyle::Solid) { - if (attribute.href_id == m_active_href_id && m_hovered_href_id == m_active_href_id) - text_color = palette().active_link(); - painter.draw_line(cell_rect.bottom_left(), cell_rect.bottom_right(), text_color); - } else if (underline_style == UnderlineStyle::Dotted) { - auto dotted_line_color = text_color.darkened(0.6f); - int x1 = cell_rect.bottom_left().x(); - int x2 = cell_rect.bottom_right().x(); - int y = cell_rect.bottom_left().y(); - for (int x = x1; x <= x2; ++x) { - if ((x % 3) == 0) - painter.set_pixel({ x, y }, dotted_line_color); - } + if (underline_style == UnderlineStyle::Solid) { + if (attribute.href_id == m_active_href_id && m_hovered_href_id == m_active_href_id) + text_color = palette().active_link(); + painter.draw_line(cell_rect.bottom_left(), cell_rect.bottom_right(), text_color); + } else if (underline_style == UnderlineStyle::Dotted) { + auto dotted_line_color = text_color.darkened(0.6f); + int x1 = cell_rect.bottom_left().x(); + int x2 = cell_rect.bottom_right().x(); + int y = cell_rect.bottom_left().y(); + for (int x = x1; x <= x2; ++x) { + if ((x % 3) == 0) + painter.set_pixel({ x, y }, dotted_line_color); } } if (codepoint == ' ') continue; - 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( character_rect.location(), codepoint, @@ -583,16 +554,16 @@ void TerminalWidget::doubleclick_event(GUI::MouseEvent& event) auto position = buffer_position_at(event.position()); auto& line = m_terminal.line(position.row()); - bool want_whitespace = line.characters()[position.column()] == ' '; + bool want_whitespace = line.codepoints()[position.column()] == ' '; int start_column = 0; int end_column = 0; - for (int column = position.column(); column >= 0 && (line.characters()[column] == ' ') == want_whitespace; --column) { + for (int column = position.column(); column >= 0 && (line.codepoints()[column] == ' ') == want_whitespace; --column) { start_column = column; } - for (int column = position.column(); column < m_terminal.columns() && (line.characters()[column] == ' ') == want_whitespace; ++column) { + for (int column = position.column(); column < m_terminal.columns() && (line.codepoints()[column] == ' ') == want_whitespace; ++column) { end_column = column; } @@ -762,7 +733,7 @@ String TerminalWidget::selected_text() const builder.append('\n'); break; } - builder.append(line.characters()[column]); + builder.append(line.codepoints()[column]); if (column == line.length() - 1 || (m_rectangle_selection && column == last_column)) { builder.append('\n'); }