1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-16 10:24:59 +00:00

GTextEditor: Implement a simple text search API

- GTextRange find(const StringView& needle, const GTextPosition& start)

This function searches for the needle in the haystack (the full text)
and returns a GTextRange for the closest match after "start".
If the needle is not found, it returns an invalid GTextRange.
If no "start" position is provided, the search begins at the head of
the text document. :^)
This commit is contained in:
Andreas Kling 2019-08-21 21:23:17 +02:00
parent 5670a3e064
commit 0c72371ad9
2 changed files with 116 additions and 14 deletions

View file

@ -668,21 +668,27 @@ int GTextEditor::content_x_for_position(const GTextPosition& position) const
}
}
Rect GTextEditor::cursor_content_rect() const
Rect GTextEditor::content_rect_for_position(const GTextPosition& position) const
{
if (!m_cursor.is_valid())
if (!position.is_valid())
return {};
ASSERT(!m_lines.is_empty());
ASSERT(m_cursor.column() <= (current_line().length() + 1));
ASSERT(position.column() <= (current_line().length() + 1));
int cursor_x = content_x_for_position(m_cursor);
int x = content_x_for_position(position);
if (is_single_line()) {
Rect cursor_rect { cursor_x, 0, 1, font().glyph_height() + 2 };
cursor_rect.center_vertically_within({ {}, frame_inner_rect().size() });
return cursor_rect;
Rect rect { x, 0, 1, font().glyph_height() + 2 };
rect.center_vertically_within({ {}, frame_inner_rect().size() });
return rect;
}
return { cursor_x, m_cursor.line() * line_height(), 1, line_height() };
return { x, position.line() * line_height(), 1, line_height() };
}
Rect GTextEditor::cursor_content_rect() const
{
return content_rect_for_position(m_cursor);
}
Rect GTextEditor::line_widget_rect(int line_index) const
@ -696,14 +702,19 @@ Rect GTextEditor::line_widget_rect(int line_index) const
return rect;
}
void GTextEditor::scroll_position_into_view(const GTextPosition& position)
{
auto rect = content_rect_for_position(position);
if (position.column() == 0)
rect.set_x(content_x_for_position({ position.line(), 0 }) - 2);
else if (position.column() == m_lines[position.line()].length())
rect.set_x(content_x_for_position({ position.line(), m_lines[position.line()].length() }) + 2);
scroll_into_view(rect, true, true);
}
void GTextEditor::scroll_cursor_into_view()
{
auto rect = cursor_content_rect();
if (m_cursor.column() == 0)
rect.set_x(content_x_for_position({ m_cursor.line(), 0 }) - 2);
else if (m_cursor.column() == m_lines[m_cursor.line()].length())
rect.set_x(content_x_for_position({ m_cursor.line(), m_lines[m_cursor.line()].length() }) + 2);
scroll_into_view(rect, true, true);
scroll_position_into_view(m_cursor);
}
Rect GTextEditor::line_content_rect(int line_index) const
@ -1079,3 +1090,64 @@ void GTextEditor::resize_event(GResizeEvent& event)
GScrollableWidget::resize_event(event);
update_content_size();
}
GTextPosition GTextEditor::next_position_after(const GTextPosition& position, ShouldWrapAtEndOfDocument should_wrap)
{
auto& line = m_lines[position.line()];
if (position.column() == line.length()) {
if (position.line() == line_count() - 1) {
if (should_wrap == ShouldWrapAtEndOfDocument::Yes)
return { 0, 0 };
return {};
}
return { position.line() + 1, 0 };
}
return { position.line(), position.column() + 1 };
}
GTextRange GTextEditor::find(const StringView& needle, const GTextPosition& start)
{
if (needle.is_empty())
return {};
GTextPosition position = start.is_valid() ? start : GTextPosition(0, 0);
GTextPosition original_position = position;
GTextPosition start_of_potential_match;
int needle_index = 0;
do {
auto ch = character_at(position);
if (ch == needle[needle_index]) {
if (needle_index == 0)
start_of_potential_match = position;
++needle_index;
if (needle_index >= needle.length())
return { start_of_potential_match, next_position_after(position) };
} else {
needle_index = 0;
}
position = next_position_after(position);
} while(position.is_valid() && position != original_position);
return {};
}
void GTextEditor::set_selection(const GTextRange& selection)
{
if (m_selection == selection)
return;
m_selection = selection;
set_cursor(m_selection.end());
scroll_position_into_view(normalized_selection().start());
update();
}
char GTextEditor::character_at(const GTextPosition& position) const
{
ASSERT(position.line() < line_count());
auto& line = m_lines[position.line()];
if (position.column() == line.length())
return '\n';
return line.characters()[position.column()];
}