diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 330ea49292f..07f4fc93f60 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -539,6 +539,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.
+
+
@@ -548,6 +554,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.
+
+
@@ -668,6 +680,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.
+
+
@@ -677,7 +695,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.
@@ -1430,6 +1448,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 ad220231500..4c6b8112551 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -4117,10 +4117,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 {
@@ -4135,6 +4149,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;
@@ -4151,11 +4200,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();
@@ -4169,6 +4220,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;
@@ -4224,6 +4276,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;
@@ -4246,6 +4299,7 @@ void TextEdit::redo() {
carets = undo_stack_pos->get().end_carets;
undo_stack_pos = undo_stack_pos->next();
+ undo_stack_idx -= 1;
_unhide_carets();
@@ -4260,6 +4314,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();
}
@@ -6662,6 +6717,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);
@@ -7013,6 +7071,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"));
@@ -7543,6 +7602,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 21c8781e264..27f96ab85a2 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -372,6 +372,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;
@@ -844,6 +845,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();