From 838fde11e3909a5b57d24d59221041616cdaf558 Mon Sep 17 00:00:00 2001 From: wareya Date: Mon, 6 Jan 2025 19:34:59 -0500 Subject: [PATCH] Add functions and signals for analyzing the undo/redo history of a TextEdit and fix docs --- doc/classes/TextEdit.xml | 27 ++++++++++++++++- scene/gui/text_edit.cpp | 62 +++++++++++++++++++++++++++++++++++++++- scene/gui/text_edit.h | 4 +++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index b884fa59586..27cab54c370 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -529,6 +529,12 @@ [b]Note:[/b] The Y position corresponds to the bottom side of the line. Use [method get_rect_at_line_column] to get the top side position. + + + + Returns the version number of the undo/redo state that will be applied if an undo is performed. + + @@ -538,6 +544,12 @@ [b]Note:[/b] The Y position of the returned rect corresponds to the top side of the line, unlike [method get_pos_at_line_column] which returns the bottom side. + + + + Returns the number of available "redo" actions in the undo/redo history. + + @@ -658,6 +670,12 @@ Returns the total number of lines in the text. This includes wrapped lines and excludes folded lines. If [member wrap_mode] is set to [constant LINE_WRAPPING_NONE] and no lines are folded (see [method CodeEdit.is_line_folded]) then this is equivalent to [method get_line_count]. See [method get_visible_line_count_in_range] for a limited range of lines. + + + + Returns the number of available "undo" actions in the undo/redo history. + + @@ -667,7 +685,7 @@ - Returns the current version of the [TextEdit]. The version is a count of recorded operations by the undo/redo history. + Returns the current version of the [TextEdit]. Different undo/redo history states have different, increasing version numbers. @@ -1417,6 +1435,13 @@ When text is added [param from_line] will be less than [param to_line]. On a remove [param to_line] will be less than [param from_line]. + + + + + Emitted when the text changes and a new undo operation has been created for the change. + + Emitted when the text changes. diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1fbd2c48c0f..aeabb91d828 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -4087,10 +4087,24 @@ void TextEdit::end_complex_operation() { undo_stack.back()->get().end_carets = carets; if (undo_stack.back()->get().chain_forward) { undo_stack.back()->get().chain_forward = false; + emit_signal(SNAME("operation_done"), undo_stack.back()->get().from_line, undo_stack.back()->get().to_line); return; } - undo_stack.back()->get().chain_backward = true; + int min_line = undo_stack.back()->get().from_line; + int max_line = undo_stack.back()->get().to_line; + + List::Element *op = undo_stack.back(); + op->get().chain_backward = true; + while (op) { + min_line = MIN(min_line, op->get().from_line); + max_line = MIN(max_line, op->get().to_line); + if (op->get().chain_forward) { + break; + } + op = op->prev(); + } + emit_signal(SNAME("operation_done"), min_line, max_line); } bool TextEdit::has_undo() const { @@ -4105,6 +4119,41 @@ bool TextEdit::has_redo() const { return undo_stack_pos != nullptr; } +int TextEdit::get_undo_count() const { + if (undo_stack_pos == nullptr) { + int pending = current_op.type == TextOperation::TYPE_NONE ? 0 : 1; + return undo_stack.size() + pending; + } + return undo_stack.size() - undo_stack_idx; +} + +int TextEdit::get_redo_count() const { + return undo_stack_idx; +} + +int TextEdit::get_prev_version() const { + if (undo_stack.is_empty()) { + return -1; + } + if (current_op.type != TextOperation::TYPE_NONE) { + return current_op.prev_version; + } + const List::Element *pos = undo_stack_pos; + if (pos == nullptr) { + pos = undo_stack.back(); + } else { + pos = pos->prev(); + } + if (pos == nullptr) { + return -1; + } + if (pos->get().chain_backward) { + while (pos && !pos->get().chain_forward) { + pos = pos->prev(); + } + } + return pos ? pos->get().prev_version : -5; +} void TextEdit::undo() { if (!editable) { return; @@ -4121,11 +4170,13 @@ void TextEdit::undo() { } undo_stack_pos = undo_stack.back(); + undo_stack_idx = 1; } else if (undo_stack_pos == undo_stack.front()) { return; // At the bottom of the undo stack. } else { undo_stack_pos = undo_stack_pos->prev(); + undo_stack_idx += 1; } deselect(); @@ -4139,6 +4190,7 @@ void TextEdit::undo() { while (true) { ERR_BREAK(!undo_stack_pos->prev()); undo_stack_pos = undo_stack_pos->prev(); + undo_stack_idx += 1; op = undo_stack_pos->get(); _do_text_op(op, true); current_op.version = op.prev_version; @@ -4194,6 +4246,7 @@ void TextEdit::redo() { while (true) { ERR_BREAK(!undo_stack_pos->next()); undo_stack_pos = undo_stack_pos->next(); + undo_stack_idx -= 1; op = undo_stack_pos->get(); _do_text_op(op, false); current_op.version = op.version; @@ -4216,6 +4269,7 @@ void TextEdit::redo() { carets = undo_stack_pos->get().end_carets; undo_stack_pos = undo_stack_pos->next(); + undo_stack_idx -= 1; _unhide_carets(); @@ -4230,6 +4284,7 @@ void TextEdit::clear_undo_history() { saved_version = 0; current_op.type = TextOperation::TYPE_NONE; undo_stack_pos = nullptr; + undo_stack_idx = 0; undo_stack.clear(); } @@ -6690,6 +6745,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("has_undo"), &TextEdit::has_undo); ClassDB::bind_method(D_METHOD("has_redo"), &TextEdit::has_redo); + ClassDB::bind_method(D_METHOD("get_undo_count"), &TextEdit::get_undo_count); + ClassDB::bind_method(D_METHOD("get_redo_count"), &TextEdit::get_redo_count); + ClassDB::bind_method(D_METHOD("get_prev_version"), &TextEdit::get_prev_version); ClassDB::bind_method(D_METHOD("undo"), &TextEdit::undo); ClassDB::bind_method(D_METHOD("redo"), &TextEdit::redo); ClassDB::bind_method(D_METHOD("clear_undo_history"), &TextEdit::clear_undo_history); @@ -7040,6 +7098,7 @@ void TextEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("text_set")); ADD_SIGNAL(MethodInfo("text_changed")); ADD_SIGNAL(MethodInfo("lines_edited_from", PropertyInfo(Variant::INT, "from_line"), PropertyInfo(Variant::INT, "to_line"))); + ADD_SIGNAL(MethodInfo("operation_done", PropertyInfo(Variant::INT, "from_line"), PropertyInfo(Variant::INT, "to_line"))); /* Caret. */ ADD_SIGNAL(MethodInfo("caret_changed")); @@ -7561,6 +7620,7 @@ void TextEdit::_clear_redo() { undo_stack_pos = undo_stack_pos->next(); undo_stack.erase(elem); } + undo_stack_idx = 0; } /* Search */ diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 6b137581f2f..926033dac8d 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -369,6 +369,7 @@ private: TextOperation current_op; List undo_stack; List::Element *undo_stack_pos = nullptr; + size_t undo_stack_idx = 0; // number of elements away from top undo_stack_pos is Timer *idle_detect = nullptr; @@ -831,6 +832,9 @@ public: bool has_undo() const; bool has_redo() const; + int get_undo_count() const; + int get_redo_count() const; + int get_prev_version() const; void undo(); void redo(); void clear_undo_history();