summaryrefslogtreecommitdiff
path: root/Libraries/LibGUI/GTextEditor.cpp
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-10-27 19:36:59 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-10-27 19:36:59 +0100
commit9b13a3905bf33c9f44879baff651ed46c8e4bc91 (patch)
treec80308b7a300cf826e82c14150582ac04d93508b /Libraries/LibGUI/GTextEditor.cpp
parentf96c683543e58d8a30ef1c2ff16df1ae4abc5bc9 (diff)
downloadserenity-9b13a3905bf33c9f44879baff651ed46c8e4bc91.zip
LibGUI: Support multiple GTextEditors editing the same GTextDocument
With this patch, you can now assign the same GTextDocument to multiple GTextEditor widgets via GTextEditor::set_document(). The editors have independent cursors and selection, but all changes are shared, and immediately propagate to all editors. This is very unoptimized and will do lots of unnecessary invalidation, especially line re-wrapping and repainting over and over again.
Diffstat (limited to 'Libraries/LibGUI/GTextEditor.cpp')
-rw-r--r--Libraries/LibGUI/GTextEditor.cpp83
1 files changed, 53 insertions, 30 deletions
diff --git a/Libraries/LibGUI/GTextEditor.cpp b/Libraries/LibGUI/GTextEditor.cpp
index 20012e4b69..9b991eaff6 100644
--- a/Libraries/LibGUI/GTextEditor.cpp
+++ b/Libraries/LibGUI/GTextEditor.cpp
@@ -19,8 +19,7 @@ GTextEditor::GTextEditor(Type type, GWidget* parent)
: GScrollableWidget(parent)
, m_type(type)
{
- m_document = GTextDocument::create(this);
- m_document->register_client(*this);
+ set_document(GTextDocument::create());
set_frame_shape(FrameShape::Container);
set_frame_shadow(FrameShadow::Sunken);
set_frame_thickness(2);
@@ -632,7 +631,7 @@ void GTextEditor::keydown_event(GKeyEvent& event)
// Backspace within line
for (int i = 0; i < erase_count; ++i) {
- current_line().remove(m_cursor.column() - 1 - i);
+ current_line().remove(document(), m_cursor.column() - 1 - i);
}
update_content_size();
set_cursor(m_cursor.line(), m_cursor.column() - erase_count);
@@ -643,9 +642,8 @@ void GTextEditor::keydown_event(GKeyEvent& event)
// Backspace at column 0; merge with previous line
auto& previous_line = lines()[m_cursor.line() - 1];
int previous_length = previous_line.length();
- previous_line.append(current_line().characters(), current_line().length());
- lines().remove(m_cursor.line());
- m_line_visual_data.remove(m_cursor.line());
+ previous_line.append(document(), current_line().characters(), current_line().length());
+ document().remove_line(m_cursor.line());
update_content_size();
update();
set_cursor(m_cursor.line() - 1, previous_length);
@@ -678,10 +676,9 @@ void GTextEditor::delete_current_line()
if (has_selection())
return delete_selection();
- lines().remove(m_cursor.line());
- m_line_visual_data.remove(m_cursor.line());
+ document().remove_line(m_cursor.line());
if (lines().is_empty())
- document().append_line(make<GTextDocumentLine>());
+ document().append_line(make<GTextDocumentLine>(document()));
update_content_size();
update();
@@ -697,7 +694,7 @@ void GTextEditor::do_delete()
if (m_cursor.column() < current_line().length()) {
// Delete within line
- current_line().remove(m_cursor.column());
+ current_line().remove(document(), m_cursor.column());
did_change();
update_cursor();
return;
@@ -706,9 +703,8 @@ void GTextEditor::do_delete()
// Delete at end of line; merge with next line
auto& next_line = lines()[m_cursor.line() + 1];
int previous_length = current_line().length();
- current_line().append(next_line.characters(), next_line.length());
- lines().remove(m_cursor.line() + 1);
- m_line_visual_data.remove(m_cursor.line() + 1);
+ current_line().append(document(), next_line.characters(), next_line.length());
+ document().remove_line(m_cursor.line() + 1);
update();
did_change();
set_cursor(m_cursor.line(), previous_length);
@@ -743,15 +739,15 @@ void GTextEditor::insert_at_cursor(char ch)
if (leading_spaces)
new_line_contents = String::repeated(' ', leading_spaces);
}
- document().insert_line(m_cursor.line() + (at_tail ? 1 : 0), make<GTextDocumentLine>(new_line_contents));
+ document().insert_line(m_cursor.line() + (at_tail ? 1 : 0), make<GTextDocumentLine>(document(), new_line_contents));
update();
did_change();
set_cursor(m_cursor.line() + 1, lines()[m_cursor.line() + 1].length());
return;
}
- auto new_line = make<GTextDocumentLine>();
- new_line->append(current_line().characters() + m_cursor.column(), current_line().length() - m_cursor.column());
- current_line().truncate(m_cursor.column());
+ auto new_line = make<GTextDocumentLine>(document());
+ new_line->append(document(), current_line().characters() + m_cursor.column(), current_line().length() - m_cursor.column());
+ current_line().truncate(document(), m_cursor.column());
document().insert_line(m_cursor.line() + 1, move(new_line));
update();
did_change();
@@ -762,13 +758,13 @@ void GTextEditor::insert_at_cursor(char ch)
int next_soft_tab_stop = ((m_cursor.column() + m_soft_tab_width) / m_soft_tab_width) * m_soft_tab_width;
int spaces_to_insert = next_soft_tab_stop - m_cursor.column();
for (int i = 0; i < spaces_to_insert; ++i) {
- current_line().insert(m_cursor.column(), ' ');
+ current_line().insert(document(), m_cursor.column(), ' ');
}
did_change();
set_cursor(m_cursor.line(), next_soft_tab_stop);
return;
}
- current_line().insert(m_cursor.column(), ch);
+ current_line().insert(document(), m_cursor.column(), ch);
did_change();
set_cursor(m_cursor.line(), m_cursor.column() + 1);
}
@@ -993,9 +989,8 @@ String GTextEditor::text() const
void GTextEditor::clear()
{
- lines().clear();
- m_line_visual_data.clear();
- document().append_line(make<GTextDocumentLine>());
+ document().remove_all_lines();
+ document().append_line(make<GTextDocumentLine>(document()));
m_selection.clear();
did_update_selection();
set_cursor(0, 0);
@@ -1030,8 +1025,7 @@ void GTextEditor::delete_selection()
// First delete all the lines in between the first and last one.
for (int i = selection.start().line() + 1; i < selection.end().line();) {
- lines().remove(i);
- m_line_visual_data.remove(i);
+ document().remove_line(i);
selection.end().set_line(selection.end().line() - 1);
}
@@ -1040,14 +1034,14 @@ void GTextEditor::delete_selection()
auto& line = lines()[selection.start().line()];
bool whole_line_is_selected = selection.start().column() == 0 && selection.end().column() == line.length();
if (whole_line_is_selected) {
- line.clear();
+ line.clear(document());
} else {
auto before_selection = String(line.characters(), line.length()).substring(0, selection.start().column());
auto after_selection = String(line.characters(), line.length()).substring(selection.end().column(), line.length() - selection.end().column());
StringBuilder builder(before_selection.length() + after_selection.length());
builder.append(before_selection);
builder.append(after_selection);
- line.set_text(builder.to_string());
+ line.set_text(document(), builder.to_string());
}
} else {
// Delete across a newline, merging lines.
@@ -1059,13 +1053,12 @@ void GTextEditor::delete_selection()
StringBuilder builder(before_selection.length() + after_selection.length());
builder.append(before_selection);
builder.append(after_selection);
- first_line.set_text(builder.to_string());
- lines().remove(selection.end().line());
- m_line_visual_data.remove(selection.end().line());
+ first_line.set_text(document(), builder.to_string());
+ document().remove_line(selection.end().line());
}
if (lines().is_empty()) {
- document().append_line(make<GTextDocumentLine>());
+ document().append_line(make<GTextDocumentLine>(document()));
}
m_selection.clear();
@@ -1415,20 +1408,50 @@ void GTextEditor::did_change_font()
void GTextEditor::document_did_append_line()
{
m_line_visual_data.append(make<LineVisualData>());
+ recompute_all_visual_lines();
+ update();
}
void GTextEditor::document_did_remove_line(int line_index)
{
m_line_visual_data.remove(line_index);
+ recompute_all_visual_lines();
+ update();
}
void GTextEditor::document_did_remove_all_lines()
{
m_line_visual_data.clear();
+ recompute_all_visual_lines();
+ update();
}
void GTextEditor::document_did_insert_line(int line_index)
{
m_line_visual_data.insert(line_index, make<LineVisualData>());
+ recompute_all_visual_lines();
+ update();
+}
+
+void GTextEditor::document_did_change()
+{
+ recompute_all_visual_lines();
+ update();
}
+void GTextEditor::set_document(GTextDocument& document)
+{
+ if (m_document.ptr() == &document)
+ return;
+ if (m_document)
+ m_document->unregister_client(*this);
+ m_document = document;
+ m_line_visual_data.clear();
+ for (int i = 0; i < m_document->line_count(); ++i) {
+ m_line_visual_data.append(make<LineVisualData>());
+ }
+ m_cursor = { 0, 0 };
+ recompute_all_visual_lines();
+ update();
+ m_document->register_client(*this);
+}