Added multi level undo redo to line edit

This commit is contained in:
Paulb23 2017-10-29 23:14:33 +00:00
parent 619e4eb23d
commit 9dddce75d0
2 changed files with 88 additions and 31 deletions

View File

@ -161,13 +161,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break; } break;
case (KEY_Z): { // Simple One level undo case (KEY_Z): { // undo / redo
if (editable) { if (editable) {
if (k->get_shift()) {
redo();
} else {
undo(); undo();
} }
}
} break; } break;
case (KEY_U): { // Delete from start to cursor case (KEY_U): { // Delete from start to cursor
@ -175,7 +176,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (editable) { if (editable) {
selection_clear(); selection_clear();
undo_text = text;
text = text.substr(cursor_pos, text.length() - cursor_pos); text = text.substr(cursor_pos, text.length() - cursor_pos);
Ref<Font> font = get_font("font"); Ref<Font> font = get_font("font");
@ -205,7 +205,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (editable) { if (editable) {
selection_clear(); selection_clear();
undo_text = text;
text = text.substr(0, cursor_pos); text = text.substr(0, cursor_pos);
_text_changed(); _text_changed();
} }
@ -245,7 +244,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
break; break;
if (selection.enabled) { if (selection.enabled) {
undo_text = text;
selection_delete(); selection_delete();
break; break;
} }
@ -276,7 +274,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
set_cursor_position(cc); set_cursor_position(cc);
} else { } else {
undo_text = text;
delete_char(); delete_char();
} }
@ -382,7 +379,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} }
if (selection.enabled) { if (selection.enabled) {
undo_text = text;
selection_delete(); selection_delete();
break; break;
} }
@ -417,7 +413,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
delete_text(cursor_pos, cc); delete_text(cursor_pos, cc);
} else { } else {
undo_text = text;
set_cursor_position(cursor_pos + 1); set_cursor_position(cursor_pos + 1);
delete_char(); delete_char();
} }
@ -778,7 +773,6 @@ void LineEdit::copy_text() {
void LineEdit::cut_text() { void LineEdit::cut_text() {
if (selection.enabled) { if (selection.enabled) {
undo_text = text;
OS::get_singleton()->set_clipboard(text.substr(selection.begin, selection.end - selection.begin)); OS::get_singleton()->set_clipboard(text.substr(selection.begin, selection.end - selection.begin));
selection_delete(); selection_delete();
} }
@ -798,23 +792,33 @@ void LineEdit::paste_text() {
} }
void LineEdit::undo() { void LineEdit::undo() {
if (undo_stack_pos == NULL) {
int old_cursor_pos = cursor_pos; if (undo_stack.size() <= 1) {
text = undo_text; return;
}
Ref<Font> font = get_font("font"); undo_stack_pos = undo_stack.back();
} else if (undo_stack_pos == undo_stack.front()) {
cached_width = 0; return;
for (int i = 0; i < text.length(); i++) }
cached_width += font->get_char_size(text[i]).width; undo_stack_pos = undo_stack_pos->prev();
TextOperation op = undo_stack_pos->get();
if (old_cursor_pos > text.length()) { text = op.text;
set_cursor_position(text.length()); set_cursor_position(op.cursor_pos);
} else { _emit_text_change();
set_cursor_position(old_cursor_pos);
} }
_text_changed(); void LineEdit::redo() {
if (undo_stack_pos == NULL) {
return;
}
if (undo_stack_pos == undo_stack.back()) {
return;
}
undo_stack_pos = undo_stack_pos->next();
TextOperation op = undo_stack_pos->get();
text = op.text;
set_cursor_position(op.cursor_pos);
_emit_text_change();
} }
void LineEdit::shift_selection_check_pre(bool p_shift) { void LineEdit::shift_selection_check_pre(bool p_shift) {
@ -947,8 +951,6 @@ void LineEdit::delete_char() {
void LineEdit::delete_text(int p_from_column, int p_to_column) { void LineEdit::delete_text(int p_from_column, int p_to_column) {
undo_text = text;
if (text.size() > 0) { if (text.size() > 0) {
Ref<Font> font = get_font("font"); Ref<Font> font = get_font("font");
if (font != NULL) { if (font != NULL) {
@ -1086,8 +1088,6 @@ void LineEdit::append_at_cursor(String p_text) {
if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) { if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) {
undo_text = text;
Ref<Font> font = get_font("font"); Ref<Font> font = get_font("font");
if (font != NULL) { if (font != NULL) {
for (int i = 0; i < p_text.length(); i++) for (int i = 0; i < p_text.length(); i++)
@ -1105,6 +1105,7 @@ void LineEdit::append_at_cursor(String p_text) {
void LineEdit::clear_internal() { void LineEdit::clear_internal() {
_clear_undo_stack();
cached_width = 0; cached_width = 0;
cursor_pos = 0; cursor_pos = 0;
window_pos = 0; window_pos = 0;
@ -1275,6 +1276,11 @@ void LineEdit::menu_option(int p_option) {
undo(); undo();
} }
} break; } break;
case MENU_REDO: {
if (editable) {
redo();
}
}
} }
} }
@ -1312,10 +1318,43 @@ void LineEdit::_text_changed() {
if (expand_to_text_length) if (expand_to_text_length)
minimum_size_changed(); minimum_size_changed();
_emit_text_change();
_clear_redo();
}
void LineEdit::_emit_text_change() {
emit_signal("text_changed", text); emit_signal("text_changed", text);
_change_notify("text"); _change_notify("text");
} }
void LineEdit::_clear_redo() {
_create_undo_state();
if (undo_stack_pos == NULL) {
return;
}
undo_stack_pos = undo_stack_pos->next();
while (undo_stack_pos) {
List<TextOperation>::Element *elem = undo_stack_pos;
undo_stack_pos = undo_stack_pos->next();
undo_stack.erase(elem);
}
_create_undo_state();
}
void LineEdit::_clear_undo_stack() {
undo_stack.clear();
undo_stack_pos = NULL;
_create_undo_state();
}
void LineEdit::_create_undo_state() {
TextOperation op;
op.text = text;
op.cursor_pos = cursor_pos;
undo_stack.push_back(op);
}
void LineEdit::_bind_methods() { void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &LineEdit::_toggle_draw_caret); ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &LineEdit::_toggle_draw_caret);
@ -1369,6 +1408,7 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_CLEAR); BIND_ENUM_CONSTANT(MENU_CLEAR);
BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_SELECT_ALL);
BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_UNDO);
BIND_ENUM_CONSTANT(MENU_REDO);
BIND_ENUM_CONSTANT(MENU_MAX); BIND_ENUM_CONSTANT(MENU_MAX);
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
@ -1388,6 +1428,8 @@ void LineEdit::_bind_methods() {
LineEdit::LineEdit() { LineEdit::LineEdit() {
undo_stack_pos = NULL;
_create_undo_state();
align = ALIGN_LEFT; align = ALIGN_LEFT;
cached_width = 0; cached_width = 0;
cursor_pos = 0; cursor_pos = 0;
@ -1421,6 +1463,7 @@ LineEdit::LineEdit() {
menu->add_item(TTR("Clear"), MENU_CLEAR); menu->add_item(TTR("Clear"), MENU_CLEAR);
menu->add_separator(); menu->add_separator();
menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
menu->add_item(TTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
menu->connect("id_pressed", this, "menu_option"); menu->connect("id_pressed", this, "menu_option");
expand_to_text_length = false; expand_to_text_length = false;
} }

View File

@ -56,6 +56,7 @@ public:
MENU_CLEAR, MENU_CLEAR,
MENU_SELECT_ALL, MENU_SELECT_ALL,
MENU_UNDO, MENU_UNDO,
MENU_REDO,
MENU_MAX MENU_MAX
}; };
@ -92,10 +93,22 @@ private:
bool drag_attempt; bool drag_attempt;
} selection; } selection;
struct TextOperation {
int cursor_pos;
String text;
};
List<TextOperation> undo_stack;
List<TextOperation>::Element *undo_stack_pos;
void _clear_undo_stack();
void _clear_redo();
void _create_undo_state();
Timer *caret_blink_timer; Timer *caret_blink_timer;
static void _ime_text_callback(void *p_self, String p_text, Point2 p_selection); static void _ime_text_callback(void *p_self, String p_text, Point2 p_selection);
void _text_changed(); void _text_changed();
void _emit_text_change();
bool expand_to_text_length; bool expand_to_text_length;
bool caret_blink_enabled; bool caret_blink_enabled;
@ -166,6 +179,7 @@ public:
void cut_text(); void cut_text();
void paste_text(); void paste_text();
void undo(); void undo();
void redo();
void set_editable(bool p_editable); void set_editable(bool p_editable);
bool is_editable() const; bool is_editable() const;