mirror of
https://github.com/RGBCube/serenity
synced 2025-05-30 21:58:10 +00:00
TextEditor+EditingEngine: Add support for the basics of Vim emulation
This commit is contained in:
parent
1c17ecdeb7
commit
b4a783d923
13 changed files with 1143 additions and 438 deletions
|
@ -24,7 +24,6 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
|
@ -32,9 +31,11 @@
|
|||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/AutocompleteProvider.h>
|
||||
#include <LibGUI/Clipboard.h>
|
||||
#include <LibGUI/EditingEngine.h>
|
||||
#include <LibGUI/InputBox.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGUI/RegularEditingEngine.h>
|
||||
#include <LibGUI/ScrollBar.h>
|
||||
#include <LibGUI/SyntaxHighlighter.h>
|
||||
#include <LibGUI/TextEditor.h>
|
||||
|
@ -82,6 +83,7 @@ TextEditor::TextEditor(Type type)
|
|||
});
|
||||
m_automatic_selection_scroll_timer->stop();
|
||||
create_actions();
|
||||
set_editing_engine(make<RegularEditingEngine>());
|
||||
}
|
||||
|
||||
TextEditor::~TextEditor()
|
||||
|
@ -599,22 +601,6 @@ void TextEditor::paint_event(PaintEvent& event)
|
|||
painter.fill_rect(cursor_content_rect(), palette().text_cursor());
|
||||
}
|
||||
|
||||
void TextEditor::toggle_selection_if_needed_for_event(const KeyEvent& event)
|
||||
{
|
||||
if (event.shift() && !m_selection.is_valid()) {
|
||||
m_selection.set(m_cursor, {});
|
||||
did_update_selection();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
if (!event.shift() && m_selection.is_valid()) {
|
||||
m_selection.clear();
|
||||
did_update_selection();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::select_all()
|
||||
{
|
||||
TextPosition start_of_document { 0, 0 };
|
||||
|
@ -625,99 +611,6 @@ void TextEditor::select_all()
|
|||
update();
|
||||
}
|
||||
|
||||
void TextEditor::get_selection_line_boundaries(size_t& first_line, size_t& last_line)
|
||||
{
|
||||
auto selection = normalized_selection();
|
||||
if (!selection.is_valid()) {
|
||||
first_line = m_cursor.line();
|
||||
last_line = m_cursor.line();
|
||||
return;
|
||||
}
|
||||
first_line = selection.start().line();
|
||||
last_line = selection.end().line();
|
||||
if (first_line != last_line && selection.end().column() == 0)
|
||||
last_line -= 1;
|
||||
}
|
||||
|
||||
void TextEditor::move_selected_lines_up()
|
||||
{
|
||||
size_t first_line;
|
||||
size_t last_line;
|
||||
get_selection_line_boundaries(first_line, last_line);
|
||||
|
||||
if (first_line == 0)
|
||||
return;
|
||||
|
||||
auto& lines = document().lines();
|
||||
lines.insert((int)last_line, lines.take((int)first_line - 1));
|
||||
m_cursor = { first_line - 1, 0 };
|
||||
|
||||
if (has_selection()) {
|
||||
m_selection.set_start({ first_line - 1, 0 });
|
||||
m_selection.set_end({ last_line - 1, line(last_line - 1).length() });
|
||||
}
|
||||
|
||||
did_change();
|
||||
update();
|
||||
}
|
||||
|
||||
void TextEditor::move_selected_lines_down()
|
||||
{
|
||||
size_t first_line;
|
||||
size_t last_line;
|
||||
get_selection_line_boundaries(first_line, last_line);
|
||||
|
||||
auto& lines = document().lines();
|
||||
ASSERT(lines.size() != 0);
|
||||
if (last_line >= lines.size() - 1)
|
||||
return;
|
||||
|
||||
lines.insert((int)first_line, lines.take((int)last_line + 1));
|
||||
m_cursor = { first_line + 1, 0 };
|
||||
|
||||
if (has_selection()) {
|
||||
m_selection.set_start({ first_line + 1, 0 });
|
||||
m_selection.set_end({ last_line + 1, line(last_line + 1).length() });
|
||||
}
|
||||
|
||||
did_change();
|
||||
update();
|
||||
}
|
||||
|
||||
static int strcmp_utf32(const u32* s1, const u32* s2, size_t n)
|
||||
{
|
||||
while (n-- > 0) {
|
||||
if (*s1++ != *s2++)
|
||||
return s1[-1] < s2[-1] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TextEditor::sort_selected_lines()
|
||||
{
|
||||
if (!is_editable())
|
||||
return;
|
||||
|
||||
if (!has_selection())
|
||||
return;
|
||||
|
||||
size_t first_line;
|
||||
size_t last_line;
|
||||
get_selection_line_boundaries(first_line, last_line);
|
||||
|
||||
auto& lines = document().lines();
|
||||
|
||||
auto start = lines.begin() + (int)first_line;
|
||||
auto end = lines.begin() + (int)last_line + 1;
|
||||
|
||||
quick_sort(start, end, [](auto& a, auto& b) {
|
||||
return strcmp_utf32(a.code_points(), b.code_points(), min(a.length(), b.length())) < 0;
|
||||
});
|
||||
|
||||
did_change();
|
||||
update();
|
||||
}
|
||||
|
||||
void TextEditor::keydown_event(KeyEvent& event)
|
||||
{
|
||||
TemporaryChange change { m_should_keep_autocomplete_box, true };
|
||||
|
@ -742,293 +635,81 @@ void TextEditor::keydown_event(KeyEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (is_single_line() && event.key() == KeyCode::Key_Tab)
|
||||
return ScrollableWidget::keydown_event(event);
|
||||
if (is_single_line()) {
|
||||
if (event.key() == KeyCode::Key_Tab)
|
||||
return ScrollableWidget::keydown_event(event);
|
||||
|
||||
if (is_single_line() && event.key() == KeyCode::Key_Return) {
|
||||
if (on_return_pressed)
|
||||
on_return_pressed();
|
||||
return;
|
||||
if (event.key() == KeyCode::Key_Return) {
|
||||
if (on_return_pressed)
|
||||
on_return_pressed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_Up) {
|
||||
if (on_up_pressed)
|
||||
on_up_pressed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_Down) {
|
||||
if (on_down_pressed)
|
||||
on_down_pressed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_PageUp) {
|
||||
if (on_pageup_pressed)
|
||||
on_pageup_pressed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_PageDown) {
|
||||
if (on_pagedown_pressed)
|
||||
on_pagedown_pressed();
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (is_multi_line()) {
|
||||
ArmedScopeGuard update_autocomplete { [&] {
|
||||
if (m_autocomplete_box && m_autocomplete_box->is_visible()) {
|
||||
m_autocomplete_provider->provide_completions([&](auto completions) {
|
||||
m_autocomplete_box->update_suggestions(move(completions));
|
||||
});
|
||||
}
|
||||
} };
|
||||
|
||||
if (!event.shift() && !event.alt() && event.ctrl() && event.key() == KeyCode::Key_Space) {
|
||||
if (m_autocomplete_provider) {
|
||||
try_show_autocomplete();
|
||||
update_autocomplete.disarm();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
ArmedScopeGuard update_autocomplete { [&] {
|
||||
if (m_autocomplete_box && m_autocomplete_box->is_visible()) {
|
||||
m_autocomplete_provider->provide_completions([&](auto completions) {
|
||||
m_autocomplete_box->update_suggestions(move(completions));
|
||||
});
|
||||
}
|
||||
} };
|
||||
if (m_editing_engine->on_key(event))
|
||||
return;
|
||||
|
||||
if (event.key() == KeyCode::Key_Escape) {
|
||||
if (on_escape_pressed)
|
||||
on_escape_pressed();
|
||||
return;
|
||||
}
|
||||
if (is_multi_line() && event.key() == KeyCode::Key_Up) {
|
||||
if (m_cursor.line() > 0 || m_line_wrapping_enabled) {
|
||||
if (event.ctrl() && event.shift()) {
|
||||
move_selected_lines_up();
|
||||
return;
|
||||
}
|
||||
TextPosition new_cursor;
|
||||
if (m_line_wrapping_enabled) {
|
||||
auto position_above = cursor_content_rect().location().translated(0, -line_height());
|
||||
new_cursor = text_position_at_content_position(position_above);
|
||||
} else {
|
||||
size_t new_line = m_cursor.line() - 1;
|
||||
size_t new_column = min(m_cursor.column(), line(new_line).length());
|
||||
new_cursor = { new_line, new_column };
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (event.key() == KeyCode::Key_Up) {
|
||||
if (on_up_pressed)
|
||||
on_up_pressed();
|
||||
|
||||
if (event.modifiers() == Mod_Shift && event.key() == KeyCode::Key_Delete) {
|
||||
if (m_autocomplete_box)
|
||||
m_autocomplete_box->close();
|
||||
return;
|
||||
}
|
||||
if (is_multi_line() && event.key() == KeyCode::Key_Down) {
|
||||
if (m_cursor.line() < (line_count() - 1) || m_line_wrapping_enabled) {
|
||||
if (event.ctrl() && event.shift()) {
|
||||
move_selected_lines_down();
|
||||
return;
|
||||
}
|
||||
TextPosition new_cursor;
|
||||
if (m_line_wrapping_enabled) {
|
||||
new_cursor = text_position_at_content_position(cursor_content_rect().location().translated(0, line_height()));
|
||||
auto position_below = cursor_content_rect().location().translated(0, line_height());
|
||||
new_cursor = text_position_at_content_position(position_below);
|
||||
} else {
|
||||
size_t new_line = m_cursor.line() + 1;
|
||||
size_t new_column = min(m_cursor.column(), line(new_line).length());
|
||||
new_cursor = { new_line, new_column };
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (event.key() == KeyCode::Key_Down) {
|
||||
if (on_down_pressed)
|
||||
on_down_pressed();
|
||||
return;
|
||||
}
|
||||
if (is_multi_line() && event.key() == KeyCode::Key_PageUp) {
|
||||
if (m_cursor.line() > 0 || m_line_wrapping_enabled) {
|
||||
TextPosition new_cursor;
|
||||
if (m_line_wrapping_enabled) {
|
||||
auto position_above = cursor_content_rect().location().translated(0, -visible_content_rect().height());
|
||||
new_cursor = text_position_at_content_position(position_above);
|
||||
} else {
|
||||
size_t page_step = (size_t)visible_content_rect().height() / (size_t)line_height();
|
||||
size_t new_line = m_cursor.line() < page_step ? 0 : m_cursor.line() - page_step;
|
||||
size_t new_column = min(m_cursor.column(), line(new_line).length());
|
||||
new_cursor = { new_line, new_column };
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (event.key() == KeyCode::Key_PageUp) {
|
||||
if (on_pageup_pressed)
|
||||
on_pageup_pressed();
|
||||
return;
|
||||
}
|
||||
if (is_multi_line() && event.key() == KeyCode::Key_PageDown) {
|
||||
if (m_cursor.line() < (line_count() - 1) || m_line_wrapping_enabled) {
|
||||
TextPosition new_cursor;
|
||||
if (m_line_wrapping_enabled) {
|
||||
auto position_below = cursor_content_rect().location().translated(0, visible_content_rect().height());
|
||||
new_cursor = text_position_at_content_position(position_below);
|
||||
} else {
|
||||
size_t new_line = min(line_count() - 1, m_cursor.line() + visible_content_rect().height() / line_height());
|
||||
size_t new_column = min(m_cursor.column(), lines()[new_line].length());
|
||||
new_cursor = { new_line, new_column };
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (event.key() == KeyCode::Key_PageDown) {
|
||||
if (on_pagedown_pressed)
|
||||
on_pagedown_pressed();
|
||||
return;
|
||||
}
|
||||
if (event.key() == KeyCode::Key_Left) {
|
||||
if (!event.shift() && m_selection.is_valid()) {
|
||||
set_cursor(m_selection.normalized().start());
|
||||
m_selection.clear();
|
||||
did_update_selection();
|
||||
if (!event.ctrl()) {
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (event.ctrl()) {
|
||||
TextPosition new_cursor;
|
||||
if (document().has_spans()) {
|
||||
auto span = document().first_non_skippable_span_before(m_cursor);
|
||||
if (span.has_value()) {
|
||||
new_cursor = span.value().range.start();
|
||||
} else {
|
||||
// No remaining spans, just use word break calculation
|
||||
new_cursor = document().first_word_break_before(m_cursor, true);
|
||||
}
|
||||
} else {
|
||||
new_cursor = document().first_word_break_before(m_cursor, true);
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_cursor.column() > 0) {
|
||||
int new_column = m_cursor.column() - 1;
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(m_cursor.line(), new_column);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
} else if (m_cursor.line() > 0) {
|
||||
int new_line = m_cursor.line() - 1;
|
||||
int new_column = lines()[new_line].length();
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_line, new_column);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.key() == KeyCode::Key_Right) {
|
||||
if (!event.shift() && m_selection.is_valid()) {
|
||||
set_cursor(m_selection.normalized().end());
|
||||
m_selection.clear();
|
||||
did_update_selection();
|
||||
if (!event.ctrl()) {
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (event.ctrl()) {
|
||||
TextPosition new_cursor;
|
||||
if (document().has_spans()) {
|
||||
auto span = document().first_non_skippable_span_after(m_cursor);
|
||||
if (span.has_value()) {
|
||||
new_cursor = 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);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
int new_line = m_cursor.line();
|
||||
int new_column = m_cursor.column();
|
||||
if (m_cursor.column() < current_line().length()) {
|
||||
new_line = m_cursor.line();
|
||||
new_column = m_cursor.column() + 1;
|
||||
} else if (m_cursor.line() != line_count() - 1) {
|
||||
new_line = m_cursor.line() + 1;
|
||||
new_column = 0;
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_line, new_column);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!event.ctrl() && event.key() == KeyCode::Key_Home) {
|
||||
TextPosition new_cursor;
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
if (m_line_wrapping_enabled) {
|
||||
// FIXME: Replicate the first_nonspace_column behavior in wrapping mode.
|
||||
auto home_position = cursor_content_rect().location().translated(-width(), 0);
|
||||
new_cursor = text_position_at_content_position(home_position);
|
||||
} else {
|
||||
size_t first_nonspace_column = current_line().first_non_whitespace_column();
|
||||
if (m_cursor.column() == first_nonspace_column) {
|
||||
new_cursor = { m_cursor.line(), 0 };
|
||||
} else {
|
||||
new_cursor = { m_cursor.line(), first_nonspace_column };
|
||||
}
|
||||
}
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!event.ctrl() && event.key() == KeyCode::Key_End) {
|
||||
TextPosition new_cursor;
|
||||
if (m_line_wrapping_enabled) {
|
||||
auto end_position = cursor_content_rect().location().translated(width(), 0);
|
||||
new_cursor = text_position_at_content_position(end_position);
|
||||
} else {
|
||||
new_cursor = { m_cursor.line(), current_line().length() };
|
||||
}
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(new_cursor);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.ctrl() && event.key() == KeyCode::Key_Home) {
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(0, 0);
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.ctrl() && event.key() == KeyCode::Key_End) {
|
||||
toggle_selection_if_needed_for_event(event);
|
||||
set_cursor(line_count() - 1, lines()[line_count() - 1].length());
|
||||
if (event.shift() && m_selection.start().is_valid()) {
|
||||
m_selection.set_end(m_cursor);
|
||||
did_update_selection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.alt() && event.shift() && event.key() == KeyCode::Key_S) {
|
||||
sort_selected_lines();
|
||||
|
||||
if (event.key() == KeyCode::Key_Delete) {
|
||||
if (m_autocomplete_box)
|
||||
m_autocomplete_box->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_Backspace) {
|
||||
if (!is_editable())
|
||||
return;
|
||||
|
@ -1069,43 +750,8 @@ void TextEditor::keydown_event(KeyEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (event.modifiers() == Mod_Shift && event.key() == KeyCode::Key_Delete) {
|
||||
if (!is_editable())
|
||||
return;
|
||||
if (m_autocomplete_box)
|
||||
m_autocomplete_box->close();
|
||||
delete_current_line();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_Delete) {
|
||||
if (!is_editable())
|
||||
return;
|
||||
if (m_autocomplete_box)
|
||||
m_autocomplete_box->close();
|
||||
do_delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.shift() && !event.alt() && event.ctrl() && event.key() == KeyCode::Key_Space) {
|
||||
if (m_autocomplete_provider) {
|
||||
try_show_autocomplete();
|
||||
update_autocomplete.disarm();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_editable() && !event.ctrl() && !event.alt() && event.code_point() != 0) {
|
||||
StringBuilder sb;
|
||||
sb.append_code_point(event.code_point());
|
||||
|
||||
if (should_autocomplete_automatically()) {
|
||||
if (sb.string_view().is_whitespace())
|
||||
m_autocomplete_timer->stop();
|
||||
else
|
||||
m_autocomplete_timer->start();
|
||||
}
|
||||
insert_at_cursor_or_replace_selection(sb.to_string());
|
||||
if (!event.ctrl() && !event.alt() && event.code_point() != 0) {
|
||||
add_code_point(event.code_point());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1156,6 +802,53 @@ void TextEditor::do_delete()
|
|||
}
|
||||
}
|
||||
|
||||
void TextEditor::add_code_point(u32 code_point)
|
||||
{
|
||||
if (!is_editable())
|
||||
return;
|
||||
|
||||
StringBuilder sb;
|
||||
sb.append_code_point(code_point);
|
||||
|
||||
if (should_autocomplete_automatically()) {
|
||||
if (sb.string_view().is_whitespace())
|
||||
m_autocomplete_timer->stop();
|
||||
else
|
||||
m_autocomplete_timer->start();
|
||||
}
|
||||
insert_at_cursor_or_replace_selection(sb.to_string());
|
||||
};
|
||||
|
||||
void TextEditor::reset_cursor_blink()
|
||||
{
|
||||
m_cursor_state = true;
|
||||
update_cursor();
|
||||
stop_timer();
|
||||
start_timer(500);
|
||||
}
|
||||
|
||||
void TextEditor::toggle_selection_if_needed_for_event(bool is_selecting)
|
||||
{
|
||||
if (is_selecting && !selection()->is_valid()) {
|
||||
selection()->set(cursor(), {});
|
||||
did_update_selection();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
if (!is_selecting && selection()->is_valid()) {
|
||||
selection()->clear();
|
||||
did_update_selection();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
if (is_selecting && selection()->start().is_valid()) {
|
||||
selection()->set_end(cursor());
|
||||
did_update_selection();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int TextEditor::content_x_for_position(const TextPosition& position) const
|
||||
{
|
||||
auto& line = this->line(position.line());
|
||||
|
@ -1208,7 +901,7 @@ Gfx::IntRect TextEditor::content_rect_for_position(const TextPosition& position)
|
|||
rect = {
|
||||
visual_line_rect.x() + x - (m_horizontal_content_padding),
|
||||
visual_line_rect.y(),
|
||||
1,
|
||||
m_editing_engine->cursor_width() == CursorWidth::WIDE ? 7 : 1,
|
||||
line_height()
|
||||
};
|
||||
return IterationDecision::Break;
|
||||
|
@ -1335,6 +1028,7 @@ void TextEditor::focusin_event(FocusEvent& event)
|
|||
select_all();
|
||||
m_cursor_state = true;
|
||||
update_cursor();
|
||||
stop_timer();
|
||||
start_timer(500);
|
||||
if (on_focusin)
|
||||
on_focusin();
|
||||
|
@ -1903,6 +1597,26 @@ void TextEditor::set_autocomplete_provider(OwnPtr<AutocompleteProvider>&& provid
|
|||
m_autocomplete_box->close();
|
||||
}
|
||||
|
||||
const EditingEngine* TextEditor::editing_engine() const
|
||||
{
|
||||
return m_editing_engine.ptr();
|
||||
}
|
||||
|
||||
void TextEditor::set_editing_engine(OwnPtr<EditingEngine> editing_engine)
|
||||
{
|
||||
if (m_editing_engine)
|
||||
m_editing_engine->detach();
|
||||
m_editing_engine = move(editing_engine);
|
||||
|
||||
ASSERT(m_editing_engine);
|
||||
m_editing_engine->attach(*this);
|
||||
|
||||
m_cursor_state = true;
|
||||
update_cursor();
|
||||
stop_timer();
|
||||
start_timer(500);
|
||||
}
|
||||
|
||||
int TextEditor::line_height() const
|
||||
{
|
||||
return font().glyph_height() + m_line_spacing;
|
||||
|
@ -1944,5 +1658,9 @@ void TextEditor::set_should_autocomplete_automatically(bool value)
|
|||
remove_child(*m_autocomplete_timer);
|
||||
m_autocomplete_timer = nullptr;
|
||||
}
|
||||
int TextEditor::number_of_visible_lines() const
|
||||
{
|
||||
return visible_content_rect().height() / line_height();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue