LibGUI: Use word breaks to intelligently navigate a TextEditor

Previously, holding Control while using the left/right arrow keys
to navigate through a TextEditor would only be helpful if the document
had spans. Now, if there are no spans, it will navigate to the
next "word break", defined to be the threshold where text changes
from alphanumeric to non-alphanumeric, or vice versa.
This commit is contained in:
FalseHonesty 2020-05-23 18:41:34 -04:00 committed by Andreas Kling
parent a3bf8c72f3
commit 5e45d68442
Notes: sideshowbarker 2024-07-19 06:11:49 +09:00

View file

@ -209,23 +209,10 @@ void TextEditor::doubleclick_event(MouseEvent& event)
auto start = text_position_at(event.position()); auto start = text_position_at(event.position());
auto end = start; auto end = start;
auto& line = this->line(start.line());
auto clicked_on_alphanumeric = isalnum(line.codepoints()[start.column()]);
if (!document().has_spans()) { if (!document().has_spans()) {
while (start.column() > 0) { start = document().first_word_break_before(start);
auto next_codepoint = line.codepoints()[start.column() - 1]; end = document().first_word_break_after(end);
if ((clicked_on_alphanumeric && !isalnum(next_codepoint)) || (!clicked_on_alphanumeric && isalnum(next_codepoint)))
break;
start.set_column(start.column() - 1);
}
while (end.column() < line.length()) {
auto next_codepoint = line.codepoints()[end.column()];
if ((clicked_on_alphanumeric && !isalnum(next_codepoint)) || (!clicked_on_alphanumeric && isalnum(next_codepoint)))
break;
end.set_column(end.column() + 1);
}
} else { } else {
for (auto& span : document().spans()) { for (auto& span : document().spans()) {
if (!span.range.contains(start)) if (!span.range.contains(start))
@ -725,12 +712,19 @@ void TextEditor::keydown_event(KeyEvent& event)
return; return;
} }
if (event.key() == KeyCode::Key_Left) { if (event.key() == KeyCode::Key_Left) {
if (event.ctrl() && document().has_spans()) { if (event.ctrl()) {
// FIXME: Do something nice when the document has no spans. TextPosition new_cursor;
if (document().has_spans()) {
auto span = document().first_non_skippable_span_before(m_cursor); auto span = document().first_non_skippable_span_before(m_cursor);
TextPosition new_cursor = !span.has_value() if (span.has_value()) {
? TextPosition(0, 0) new_cursor = span.value().range.start();
: span.value().range.start(); } else {
// No remaining spans, just use word break calculation
new_cursor = document().first_word_break_before(m_cursor);
}
} else {
new_cursor = document().first_word_break_before(m_cursor);
}
toggle_selection_if_needed_for_event(event); toggle_selection_if_needed_for_event(event);
set_cursor(new_cursor); set_cursor(new_cursor);
if (event.shift() && m_selection.start().is_valid()) { if (event.shift() && m_selection.start().is_valid()) {
@ -760,12 +754,19 @@ void TextEditor::keydown_event(KeyEvent& event)
return; return;
} }
if (event.key() == KeyCode::Key_Right) { if (event.key() == KeyCode::Key_Right) {
if (event.ctrl() && document().has_spans()) { if (event.ctrl()) {
// FIXME: Do something nice when the document has no spans. TextPosition new_cursor;
if (document().has_spans()) {
auto span = document().first_non_skippable_span_after(m_cursor); auto span = document().first_non_skippable_span_after(m_cursor);
TextPosition new_cursor = !span.has_value() if (span.has_value()) {
? document().spans().last().range.end() new_cursor = span.value().range.start();
: span.value().range.start(); } else {
// No remaining spans, just use word break calculation
new_cursor = document().first_word_break_after(m_cursor);
}
} else {
new_cursor = document().first_word_break_after(m_cursor);
}
toggle_selection_if_needed_for_event(event); toggle_selection_if_needed_for_event(event);
set_cursor(new_cursor); set_cursor(new_cursor);
if (event.shift() && m_selection.start().is_valid()) { if (event.shift() && m_selection.start().is_valid()) {