Terminal: Add some basic emoji support

This is not as perfect as it is elsewhere in the system, as we cannot
really change how terminal "thinks about" characters and bytes. What
we can do though, and what this commit does, is to *render* emojis, but
make it seem as if they take up all the space, and all the columns their
bytes would take if they were all regular characters.
This commit is contained in:
Sergey Bugaev 2019-09-04 23:51:47 +03:00 committed by Andreas Kling
parent 22e6978c71
commit 6d3f52c4a4
Notes: sideshowbarker 2024-07-19 12:15:46 +09:00
2 changed files with 50 additions and 12 deletions

View File

@ -225,21 +225,57 @@ void TerminalWidget::paint_event(GPaintEvent& event)
painter.fill_rect(row_rect, Color::Red);
else if (has_only_one_background_color)
painter.fill_rect(row_rect, lookup_color(line.attributes[0].background_color).with_alpha(m_opacity));
for (u16 column = 0; column < m_terminal.columns(); ++column) {
char ch = line.characters[column];
bool should_reverse_fill_for_cursor_or_selection = (m_cursor_blink_state && m_in_active_window && row == row_with_cursor && column == m_terminal.cursor_column())
|| selection_contains({ row, column });
auto& attribute = line.attributes[column];
auto character_rect = glyph_rect(row, column);
if (!has_only_one_background_color || should_reverse_fill_for_cursor_or_selection) {
auto cell_rect = character_rect.inflated(0, m_line_spacing);
painter.fill_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.foreground_color : attribute.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.
ASSERT(line.text().length() == m_terminal.columns());
Utf8View utf8_view { line.text() };
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);
// 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;
for (u16 column = this_char_column; column < next_char_column; ++column) {
should_reverse_fill_for_cursor_or_selection |=
m_cursor_blink_state
&& m_in_active_window
&& row == row_with_cursor
&& column == m_terminal.cursor_column();
should_reverse_fill_for_cursor_or_selection |= selection_contains({ row, column });
attribute = line.attributes[column];
auto character_rect = glyph_rect(row, column);
if (!has_only_one_background_color || should_reverse_fill_for_cursor_or_selection) {
auto cell_rect = character_rect.inflated(0, m_line_spacing);
painter.fill_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.foreground_color : attribute.background_color).with_alpha(m_opacity));
}
}
if (ch == ' ')
if (codepoint == ' ')
continue;
painter.draw_glyph(
auto character_rect = glyph_rect(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(),
ch,
codepoint,
attribute.flags & VT::Attribute::Bold ? Font::default_bold_fixed_width_font() : font(),
lookup_color(should_reverse_fill_for_cursor_or_selection ? attribute.background_color : attribute.foreground_color));
}

View File

@ -82,6 +82,8 @@ public:
void clear(Attribute);
bool has_only_one_background_color() const;
void set_length(u16);
StringView text() const { return { characters, m_length }; }
u8* characters { nullptr };
Attribute* attributes { nullptr };
bool dirty { false };