mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 06:18:14 +00:00
LibGUI: Implement trailing whitespace visualization in TextEditor
This patch adds an optional mode where TextEditor highlights trailing whitespace characters on each line with a nice reddish dither pattern. We should probably make this themable and I'm sure it could be nicer somehow, but this is just a first cut and I do kinda like it. :^)
This commit is contained in:
parent
08f1ea3e45
commit
aa70d8c217
4 changed files with 52 additions and 0 deletions
|
@ -94,6 +94,23 @@ size_t TextDocumentLine::first_non_whitespace_column() const
|
||||||
return length();
|
return length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<size_t> TextDocumentLine::last_non_whitespace_column() const
|
||||||
|
{
|
||||||
|
for (ssize_t i = length() - 1; i >= 0; --i) {
|
||||||
|
auto code_point = code_points()[i];
|
||||||
|
if (!isspace(code_point))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextDocumentLine::ends_in_whitespace() const
|
||||||
|
{
|
||||||
|
if (!length())
|
||||||
|
return false;
|
||||||
|
return isspace(code_points()[length() - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
String TextDocumentLine::to_utf8() const
|
String TextDocumentLine::to_utf8() const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
|
|
|
@ -177,6 +177,8 @@ public:
|
||||||
void remove_range(TextDocument&, size_t start, size_t length);
|
void remove_range(TextDocument&, size_t start, size_t length);
|
||||||
|
|
||||||
size_t first_non_whitespace_column() const;
|
size_t first_non_whitespace_column() const;
|
||||||
|
Optional<size_t> last_non_whitespace_column() const;
|
||||||
|
bool ends_in_whitespace() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// NOTE: This vector is null terminated.
|
// NOTE: This vector is null terminated.
|
||||||
|
|
|
@ -495,6 +495,26 @@ void TextEditor::paint_event(PaintEvent& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_visualize_trailing_whitespace && line.ends_in_whitespace()) {
|
||||||
|
size_t physical_column;
|
||||||
|
auto last_non_whitespace_column = line.last_non_whitespace_column();
|
||||||
|
if (last_non_whitespace_column.has_value())
|
||||||
|
physical_column = last_non_whitespace_column.value() + 1;
|
||||||
|
else
|
||||||
|
physical_column = 0;
|
||||||
|
size_t end_of_visual_line = (start_of_visual_line + visual_line_text.length());
|
||||||
|
if (physical_column < end_of_visual_line) {
|
||||||
|
size_t visual_column = physical_column > start_of_visual_line ? (physical_column - start_of_visual_line) : 0;
|
||||||
|
Gfx::IntRect whitespace_rect {
|
||||||
|
content_x_for_position({ line_index, visual_column }),
|
||||||
|
visual_line_rect.y(),
|
||||||
|
font().width(visual_line_text.substring_view(visual_column, visual_line_text.length() - visual_column)),
|
||||||
|
visual_line_rect.height()
|
||||||
|
};
|
||||||
|
painter.fill_rect_with_dither_pattern(whitespace_rect, Color(), Color(255, 192, 192));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (physical_line_has_selection) {
|
if (physical_line_has_selection) {
|
||||||
size_t start_of_selection_within_visual_line = (size_t)max(0, (int)selection_start_column_within_line - (int)start_of_visual_line);
|
size_t start_of_selection_within_visual_line = (size_t)max(0, (int)selection_start_column_within_line - (int)start_of_visual_line);
|
||||||
size_t end_of_selection_within_visual_line = selection_end_column_within_line - start_of_visual_line;
|
size_t end_of_selection_within_visual_line = selection_end_column_within_line - start_of_visual_line;
|
||||||
|
@ -536,6 +556,7 @@ void TextEditor::paint_event(PaintEvent& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++visual_line_index;
|
++visual_line_index;
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
@ -1718,4 +1739,12 @@ void TextEditor::set_icon(const Gfx::Bitmap* icon)
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextEditor::set_visualize_trailing_whitespace(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_visualize_trailing_whitespace == enabled)
|
||||||
|
return;
|
||||||
|
m_visualize_trailing_whitespace = enabled;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,9 @@ public:
|
||||||
|
|
||||||
virtual void set_document(TextDocument&);
|
virtual void set_document(TextDocument&);
|
||||||
|
|
||||||
|
void set_visualize_trailing_whitespace(bool);
|
||||||
|
bool visualize_trailing_whitespace() const { return m_visualize_trailing_whitespace; }
|
||||||
|
|
||||||
bool has_visible_list() const { return m_has_visible_list; }
|
bool has_visible_list() const { return m_has_visible_list; }
|
||||||
void set_has_visible_list(bool);
|
void set_has_visible_list(bool);
|
||||||
bool has_open_button() const { return m_has_open_button; }
|
bool has_open_button() const { return m_has_open_button; }
|
||||||
|
@ -274,6 +277,7 @@ private:
|
||||||
bool m_line_wrapping_enabled { false };
|
bool m_line_wrapping_enabled { false };
|
||||||
bool m_has_visible_list { false };
|
bool m_has_visible_list { false };
|
||||||
bool m_has_open_button { false };
|
bool m_has_open_button { false };
|
||||||
|
bool m_visualize_trailing_whitespace { true };
|
||||||
int m_line_spacing { 4 };
|
int m_line_spacing { 4 };
|
||||||
size_t m_soft_tab_width { 4 };
|
size_t m_soft_tab_width { 4 };
|
||||||
int m_horizontal_content_padding { 3 };
|
int m_horizontal_content_padding { 3 };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue