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();