mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 19:45:08 +00:00
TextEditor: Added redo functionality & proper stack manipulation
Added redo functionality & added m_undo_stack_index that moves back & forth on the stack depending on the undo's & redo's.
This commit is contained in:
parent
306cd0d840
commit
96c1e36ed3
2 changed files with 97 additions and 44 deletions
|
@ -29,6 +29,13 @@ GTextEditor::GTextEditor(Type type, GWidget* parent)
|
|||
vertical_scrollbar().set_step(line_height());
|
||||
m_cursor = { 0, 0 };
|
||||
create_actions();
|
||||
|
||||
// TODO: Instead of a repating timer, this we should call a delayed 2 sec timer when the user types.
|
||||
m_undo_timer = CTimer::construct(
|
||||
2000, [&] {
|
||||
update_undo_timer();
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
GTextEditor::~GTextEditor()
|
||||
|
@ -456,14 +463,15 @@ void GTextEditor::select_all()
|
|||
|
||||
void GTextEditor::undo()
|
||||
{
|
||||
if (m_undo_stack.size() <= 0)
|
||||
if (m_undo_stack_index >= m_undo_stack.size() || m_undo_stack.is_empty())
|
||||
return;
|
||||
|
||||
auto& undo_vector = m_undo_stack[m_undo_index];
|
||||
auto& undo_container = m_undo_stack[m_undo_stack_index];
|
||||
auto& undo_vector = undo_container.m_undo_vector;
|
||||
|
||||
//If we try to undo a empty vector, delete it and skip over.
|
||||
if (undo_vector.size() <= 0 && m_undo_index > 0) {
|
||||
m_undo_index--;
|
||||
if (undo_vector.is_empty()) {
|
||||
m_undo_stack.remove(m_undo_stack_index);
|
||||
undo();
|
||||
return;
|
||||
}
|
||||
|
@ -473,14 +481,23 @@ void GTextEditor::undo()
|
|||
undo_command.undo();
|
||||
}
|
||||
|
||||
undo_vector.clear();
|
||||
m_undo_stack.remove(m_undo_index);
|
||||
if (m_undo_index > 0)
|
||||
m_undo_index--;
|
||||
m_undo_stack_index++;
|
||||
}
|
||||
|
||||
void GTextEditor::redo()
|
||||
{
|
||||
if (m_undo_stack_index <= 0 || m_undo_stack.is_empty())
|
||||
return;
|
||||
|
||||
auto& undo_container = m_undo_stack[m_undo_stack_index - 1];
|
||||
auto& redo_vector = undo_container.m_undo_vector;
|
||||
|
||||
for (int i = redo_vector.size() - 1; i >= 0; i--) {
|
||||
auto& undo_command = redo_vector[i];
|
||||
undo_command.redo();
|
||||
}
|
||||
|
||||
m_undo_stack_index--;
|
||||
}
|
||||
|
||||
void GTextEditor::keydown_event(GKeyEvent& event)
|
||||
|
@ -933,23 +950,23 @@ void GTextEditor::update_cursor()
|
|||
|
||||
void GTextEditor::update_undo_timer()
|
||||
{
|
||||
if (m_undo_stack.size() <= 0)
|
||||
if (m_undo_stack.is_empty())
|
||||
return;
|
||||
|
||||
if (m_undo_timer == 0)
|
||||
m_prev_undo_stack_size = m_undo_stack[m_undo_index].size();
|
||||
auto& undo_vector = m_undo_stack[0].m_undo_vector;
|
||||
|
||||
if (m_undo_timer >= 2 && m_undo_stack[m_undo_index].size() > 0) {
|
||||
if (undo_vector.size() == m_last_updated_undo_vector_size && !undo_vector.is_empty()) {
|
||||
auto undo_commands_container = make<UndoCommandsContainer>();
|
||||
m_undo_stack.prepend(move(undo_commands_container));
|
||||
// Note: Remove dbg() if we're 100% sure there are no bugs left.
|
||||
dbg() << "Undo stack increased to " << m_undo_stack.size();
|
||||
|
||||
if (m_undo_stack[m_undo_index].size() == m_prev_undo_stack_size) {
|
||||
dbg() << "Increased Undo Index";
|
||||
m_undo_stack.append(make<NonnullOwnPtrVector<UndoCommand>>());
|
||||
m_undo_index++;
|
||||
}
|
||||
m_undo_timer = -1;
|
||||
// Shift the index to the left since we're adding an empty container.
|
||||
if (m_undo_stack_index > 0)
|
||||
m_undo_stack_index++;
|
||||
}
|
||||
|
||||
m_undo_timer++;
|
||||
m_last_updated_undo_vector_size = undo_vector.size();
|
||||
}
|
||||
|
||||
void GTextEditor::set_cursor(int line, int column)
|
||||
|
@ -998,10 +1015,8 @@ void GTextEditor::focusout_event(CEvent&)
|
|||
void GTextEditor::timer_event(CTimerEvent&)
|
||||
{
|
||||
m_cursor_state = !m_cursor_state;
|
||||
if (is_focused()) {
|
||||
if (is_focused())
|
||||
update_cursor();
|
||||
update_undo_timer();
|
||||
}
|
||||
}
|
||||
|
||||
bool GTextEditor::write_to_file(const StringView& path)
|
||||
|
@ -1296,12 +1311,29 @@ void GTextEditor::recompute_all_visual_lines()
|
|||
update_content_size();
|
||||
}
|
||||
|
||||
void GTextEditor::ensure_cursor_is_valid()
|
||||
{
|
||||
if (cursor().column() > lines()[cursor().line()].length())
|
||||
set_cursor(cursor().line(), cursor().column() - (lines()[cursor().line()].length() - cursor().column()));
|
||||
}
|
||||
|
||||
void GTextEditor::add_to_undo_stack(NonnullOwnPtr<UndoCommand> undo_command)
|
||||
{
|
||||
if (m_undo_stack.size() <= m_undo_index)
|
||||
m_undo_stack.append(make<NonnullOwnPtrVector<UndoCommand>>());
|
||||
if (m_undo_stack.is_empty()) {
|
||||
auto undo_commands_container = make<UndoCommandsContainer>();
|
||||
m_undo_stack.prepend(move(undo_commands_container));
|
||||
}
|
||||
|
||||
m_undo_stack[(m_undo_index)].insert(0, move(undo_command));
|
||||
// Clear the elements of the stack before the m_undo_stack_index (Excluding our new element)
|
||||
for (int i = 1; i < m_undo_stack_index; i++)
|
||||
m_undo_stack.remove(1);
|
||||
|
||||
if (m_undo_stack_index > 0 && !m_undo_stack.is_empty())
|
||||
m_undo_stack[0].m_undo_vector.clear();
|
||||
|
||||
m_undo_stack_index = 0;
|
||||
|
||||
m_undo_stack[0].m_undo_vector.prepend(move(undo_command));
|
||||
}
|
||||
|
||||
int GTextEditor::visual_line_containing(int line_index, int column) const
|
||||
|
@ -1494,16 +1526,13 @@ GTextEditor::CreateLineCommand::CreateLineCommand(GTextEditor& text_editor, Vect
|
|||
|
||||
void GTextEditor::InsertCharacterCommand::undo()
|
||||
{
|
||||
//Move back the cursor if it's inside in deleted content
|
||||
if (m_text_editor.cursor().column() >= m_text_position.column())
|
||||
m_text_editor.set_cursor(m_text_position.line(), m_text_position.column() - 1);
|
||||
|
||||
m_text_editor.lines()[m_text_position.line()].remove(m_text_editor.document(), (m_text_position.column() - 1));
|
||||
m_text_editor.ensure_cursor_is_valid();
|
||||
}
|
||||
|
||||
void GTextEditor::InsertCharacterCommand::redo()
|
||||
{
|
||||
//TOOD: Redo implementation
|
||||
m_text_editor.lines()[m_text_position.line()].insert(m_text_editor.document(), m_text_position.column() - 1, m_character);
|
||||
}
|
||||
|
||||
void GTextEditor::RemoveCharacterCommand::undo()
|
||||
|
@ -1513,40 +1542,58 @@ void GTextEditor::RemoveCharacterCommand::undo()
|
|||
|
||||
void GTextEditor::RemoveCharacterCommand::redo()
|
||||
{
|
||||
//TOOD: Redo implementation
|
||||
m_text_editor.lines()[m_text_position.line()].remove(m_text_editor.document(), (m_text_position.column()));
|
||||
m_text_editor.ensure_cursor_is_valid();
|
||||
}
|
||||
|
||||
void GTextEditor::RemoveLineCommand::undo()
|
||||
{
|
||||
|
||||
//Insert back the line
|
||||
// Insert back the line
|
||||
m_text_editor.document().insert_line(m_text_position.line(), make<GTextDocumentLine>(m_text_editor.document(), m_line_content));
|
||||
|
||||
//Remove the merged line contents
|
||||
if (m_has_merged_content)
|
||||
// Remove the merged line contents
|
||||
if (m_has_merged_content) {
|
||||
for (int i = m_line_content.length() - 1; i >= 0; i--)
|
||||
m_text_editor.document().lines()[m_text_position.line() - 1].remove(m_text_editor.document(), (m_text_position.column()) + i);
|
||||
}
|
||||
}
|
||||
|
||||
void GTextEditor::RemoveLineCommand::redo()
|
||||
{
|
||||
//TOOD: Redo implementation
|
||||
// Remove the created line
|
||||
m_text_editor.document().remove_line(m_text_position.line());
|
||||
|
||||
// Add back the line contents
|
||||
if (m_has_merged_content) {
|
||||
for (int i = 0; i < m_line_content.length(); i++)
|
||||
m_text_editor.document().lines()[m_text_position.line() - 1].insert(m_text_editor.document(), (m_text_position.column()) + i, m_line_content[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void GTextEditor::CreateLineCommand::undo()
|
||||
{
|
||||
//Insert back the created line portion
|
||||
// Insert back the created line portion
|
||||
for (int i = 0; i < m_line_content.size(); i++)
|
||||
m_text_editor.document().lines()[m_text_position.line()].insert(m_text_editor.document(), (m_text_position.column() - 1) + i, m_line_content[i]);
|
||||
|
||||
//Set the cursor back before the selection
|
||||
// Move the cursor up a row back before the split.
|
||||
m_text_editor.set_cursor(m_text_position.line(), m_text_editor.document().lines()[m_text_position.line()].length());
|
||||
|
||||
//Remove the created line
|
||||
// Remove the created line
|
||||
m_text_editor.document().remove_line(m_text_position.line() + 1);
|
||||
}
|
||||
|
||||
void GTextEditor::CreateLineCommand::redo()
|
||||
{
|
||||
//TOOD: Redo implementation
|
||||
// Remove the characters that we're inserted back
|
||||
for (int i = m_line_content.size() - 1; i >= 0; i--)
|
||||
m_text_editor.document().lines()[m_text_position.line()].remove(m_text_editor.document(), (m_text_position.column()) + i);
|
||||
|
||||
m_text_editor.ensure_cursor_is_valid();
|
||||
|
||||
// Then we want to add BACK the created line
|
||||
m_text_editor.document().insert_line(m_text_position.line() + 1, make<GTextDocumentLine>(m_text_editor.document(), ""));
|
||||
|
||||
for (int i = 0; i < m_line_content.size(); i++)
|
||||
m_text_editor.document().lines()[m_text_position.line() + 1].insert(m_text_editor.document(), i, m_line_content[i]);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue