summaryrefslogtreecommitdiff
path: root/scene/gui/text_edit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/text_edit.cpp')
-rw-r--r--scene/gui/text_edit.cpp374
1 files changed, 330 insertions, 44 deletions
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index d84e9956ba..0b797e7df3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -46,7 +46,6 @@
#define TAB_PIXELS
-
static bool _is_text_char(CharType c) {
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
@@ -57,6 +56,42 @@ static bool _is_symbol(CharType c) {
return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t');
}
+static bool _is_pair_right_symbol(CharType c) {
+ return
+ c == '"' ||
+ c == '\'' ||
+ c == ')' ||
+ c == ']' ||
+ c == '}';
+}
+
+static bool _is_pair_left_symbol(CharType c) {
+ return
+ c == '"' ||
+ c == '\'' ||
+ c == '(' ||
+ c == '[' ||
+ c == '{';
+}
+
+static bool _is_pair_symbol(CharType c) {
+ return _is_pair_left_symbol(c) || _is_pair_right_symbol(c);
+}
+
+static CharType _get_right_pair_symbol(CharType c) {
+ if(c == '"')
+ return '"';
+ if(c == '\'')
+ return '\'';
+ if(c == '(')
+ return ')';
+ if(c == '[')
+ return ']';
+ if(c == '{')
+ return '}';
+ return 0;
+}
+
void TextEdit::Text::set_font(const Ref<Font>& p_font) {
font=p_font;
@@ -301,7 +336,7 @@ void TextEdit::_update_scrollbars() {
v_scroll->set_val(cursor.line_ofs);
} else {
-
+ cursor.line_ofs = 0;
v_scroll->hide();
}
@@ -707,6 +742,93 @@ void TextEdit::_notification(int p_what) {
}
}
+void TextEdit::_consume_pair_symbol(CharType ch) {
+
+ int cursor_position_to_move = cursor_get_column() + 1;
+
+ CharType ch_single[2] = {ch, 0};
+ CharType ch_single_pair[2] = {_get_right_pair_symbol(ch), 0};
+ CharType ch_pair[3] = {ch, _get_right_pair_symbol(ch), 0};
+
+ if(is_selection_active()) {
+
+ int new_column,new_line;
+
+ _begin_compex_operation();
+ _insert_text(get_selection_from_line(), get_selection_from_column(),
+ ch_single,
+ &new_line, &new_column);
+
+ int to_col_offset = 0;
+ if(get_selection_from_line() == get_selection_to_line())
+ to_col_offset = 1;
+
+ _insert_text(get_selection_to_line(),
+ get_selection_to_column() + to_col_offset,
+ ch_single_pair,
+ &new_line,&new_column);
+ _end_compex_operation();
+
+ cursor_set_line(get_selection_to_line());
+ cursor_set_column(get_selection_to_column() + to_col_offset);
+
+ deselect();
+ update();
+ return;
+ }
+
+ if( (ch == '\'' || ch == '"') &&
+ cursor_get_column() > 0 &&
+ _is_text_char(text[cursor.line][cursor_get_column() - 1])
+ ) {
+ insert_text_at_cursor(ch_single);
+ cursor_set_column(cursor_position_to_move);
+ return;
+ }
+
+ if(cursor_get_column() < text[cursor.line].length()) {
+ if(_is_text_char(text[cursor.line][cursor_get_column()])) {
+ insert_text_at_cursor(ch_single);
+ cursor_set_column(cursor_position_to_move);
+ return;
+ }
+ if( _is_pair_right_symbol(ch) &&
+ text[cursor.line][cursor_get_column()] == ch
+ ) {
+ cursor_set_column(cursor_position_to_move);
+ return;
+ }
+ }
+
+
+ insert_text_at_cursor(ch_pair);
+ cursor_set_column(cursor_position_to_move);
+ return;
+
+}
+
+void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column) {
+
+ bool remove_right_symbol = false;
+
+ if(cursor.column < text[cursor.line].length() && cursor.column > 0) {
+
+ CharType left_char = text[cursor.line][cursor.column - 1];
+ CharType right_char = text[cursor.line][cursor.column];
+
+ if(right_char == _get_right_pair_symbol(left_char)) {
+ remove_right_symbol = true;
+ }
+
+ }
+ if(remove_right_symbol) {
+ _remove_text(prev_line,prev_column,cursor.line,cursor.column + 1);
+ } else {
+ _remove_text(prev_line,prev_column,cursor.line,cursor.column);
+ }
+
+}
+
void TextEdit::backspace_at_cursor() {
if (cursor.column==0 && cursor.line==0)
@@ -714,7 +836,14 @@ void TextEdit::backspace_at_cursor() {
int prev_line = cursor.column?cursor.line:cursor.line-1;
int prev_column = cursor.column?(cursor.column-1):(text[cursor.line-1].length());
- _remove_text(prev_line,prev_column,cursor.line,cursor.column);
+ if(auto_brace_completion_enabled &&
+ cursor.column > 0 &&
+ _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) {
+ _consume_backspace_for_pair_symbol(prev_line, prev_column);
+ } else {
+ _remove_text(prev_line,prev_column,cursor.line,cursor.column);
+ }
+
cursor_set_line(prev_line);
cursor_set_column(prev_column);
@@ -976,7 +1105,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
return;
}
- if (k.scancode==KEY_RETURN) {
+ if (k.scancode==KEY_RETURN || k.scancode==KEY_TAB) {
_confirm_completion();
accept_event();
@@ -1002,11 +1131,17 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (cursor.column<text[cursor.line].length() && text[cursor.line][cursor.column]==k.unicode) {
//same char, move ahead
cursor_set_column(cursor.column+1);
+
} else {
//different char, go back
const CharType chr[2] = {k.unicode, 0};
- _insert_text_at_cursor(chr);
+ if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
+ _consume_pair_symbol(chr[0]);
+ } else {
+ _insert_text_at_cursor(chr);
+ }
}
+
_update_completion_candidates();
accept_event();
@@ -1072,8 +1207,10 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_line(selection.from_line);
cursor_set_column(selection.from_column);
+ _begin_compex_operation();
_remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
_insert_text_at_cursor(txt);
+ _end_compex_operation();
selection.active=true;
selection.from_column=sel_column;
selection.from_line=sel_line;
@@ -1111,7 +1248,8 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
default:
if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta)
clear=true;
-
+ if (auto_brace_completion_enabled && _is_pair_left_symbol(k.unicode))
+ clear=false;
}
if (unselect) {
@@ -1216,13 +1354,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_column(cc);
- } else if (cursor.column==0) {
+ } else if (cursor.column==0) {
if (cursor.line>0) {
cursor_set_line(cursor.line-1);
cursor_set_column(text[cursor.line].length());
}
- } else {
+ } else {
cursor_set_column(cursor_get_column()-1);
}
@@ -1256,13 +1394,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_column(cc);
- } else if (cursor.column==text[cursor.line].length()) {
+ } else if (cursor.column==text[cursor.line].length()) {
if (cursor.line<text.size()-1) {
cursor_set_line(cursor.line+1);
cursor_set_column(0);
}
- } else {
+ } else {
cursor_set_column(cursor_get_column()+1);
}
@@ -1431,19 +1569,35 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
break;
}
- if (!selection.active)
- break;
+ if (!selection.active){
- String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- OS::get_singleton()->set_clipboard(clipboard);
+ String clipboard = text[cursor.line];
+ OS::get_singleton()->set_clipboard(clipboard);
+ cursor_set_line(cursor.line);
+ cursor_set_column(0);
+ _remove_text(cursor.line,0,cursor.line,text[cursor.line].length());
- cursor_set_line(selection.from_line);
- cursor_set_column(selection.from_column);
+ backspace_at_cursor();
+ update();
+ cursor_set_line(cursor.line+1);
+ cut_copy_line = true;
- _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- selection.active=false;
- selection.selecting_mode=Selection::MODE_NONE;
- update();
+ }
+ else
+ {
+
+ String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
+ OS::get_singleton()->set_clipboard(clipboard);
+
+ cursor_set_line(selection.from_line);
+ cursor_set_column(selection.from_column);
+
+ _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
+ selection.active=false;
+ selection.selecting_mode=Selection::MODE_NONE;
+ update();
+ cut_copy_line = false;
+ }
} break;
case KEY_C: {
@@ -1453,11 +1607,16 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
break;
}
- if (!selection.active)
- break;
-
- String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- OS::get_singleton()->set_clipboard(clipboard);
+ if (!selection.active){
+ String clipboard = _base_get_text(cursor.line,0,cursor.line,text[cursor.line].length());
+ OS::get_singleton()->set_clipboard(clipboard);
+ cut_copy_line = true;
+ }
+ else{
+ String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
+ OS::get_singleton()->set_clipboard(clipboard);
+ cut_copy_line = false;
+ }
} break;
case KEY_Z: {
@@ -1487,6 +1646,12 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_column(selection.from_column);
}
+ else if (cut_copy_line)
+ {
+ cursor_set_column(0);
+ String ins="\n";
+ clipboard += ins;
+ }
_insert_text_at_cursor(clipboard);
@@ -1503,10 +1668,54 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
} break;
- default: {
-
- scancode_handled=false;
- } break;
+ case KEY_K:{
+ if (!k.mod.command || k.mod.shift || k.mod.alt) {
+ scancode_handled=false;
+ break;
+ }
+ else {
+ if (selection.active) {
+ int ini = selection.from_line;
+ int end = selection.to_line;
+ for (int i=ini; i<= end; i++)
+ {
+ _insert_text(i,0,"#");
+ }
+ }
+ else{
+ _insert_text(cursor.line,0,"#");
+ }
+ update();
+ }
+ break;}
+
+ case KEY_U:{
+ if (!k.mod.command || k.mod.shift || k.mod.alt) {
+ scancode_handled=false;
+ break;
+ }
+ else {
+ if (selection.active) {
+ int ini = selection.from_line;
+ int end = selection.to_line;
+ for (int i=ini; i<= end; i++)
+ {
+ if (text[i][0] == '#')
+ _remove_text(i,0,i,1);
+ }
+ }
+ else{
+ if (text[cursor.line][0] == '#')
+ _remove_text(cursor.line,0,cursor.line,1);
+ }
+ update();
+ }
+ break;}
+
+ default: {
+
+ scancode_handled=false;
+ } break;
}
@@ -1520,14 +1729,35 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (readonly)
break;
+ accept_event();
+ } else {
+
+ break;
+ }
+ }
+
+ if (!scancode_handled && !k.mod.command && !k.mod.alt) {
+
+ if (k.unicode>=32) {
+
+ if (readonly)
+ break;
+
const CharType chr[2] = {k.unicode, 0};
- _insert_text_at_cursor(chr);
+
+ if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
+ _consume_pair_symbol(chr[0]);
+ } else {
+ _insert_text_at_cursor(chr);
+ }
+
accept_event();
} else {
break;
}
}
+
if (!selection.selecting_test) {
@@ -1569,8 +1799,6 @@ void TextEdit::_post_shift_selection() {
/**** TEXT EDIT CORE API ****/
-
-
void TextEdit::_base_insert_text(int p_line, int p_char,const String& p_text,int &r_end_line,int &r_end_column) {
//save for undo...
@@ -1677,14 +1905,12 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column,int p_to_lin
-
void TextEdit::_insert_text(int p_line, int p_char,const String& p_text,int *r_end_line,int *r_end_column) {
if (!setting_text)
idle_detect->start();
if (undo_enabled) {
-
_clear_redo();
}
@@ -1868,7 +2094,6 @@ void TextEdit::adjust_viewport_to_cursor() {
}
-
void TextEdit::cursor_set_column(int p_col) {
if (p_col<0)
@@ -2336,6 +2561,27 @@ String TextEdit::get_selection_text() const {
}
+String TextEdit::get_word_under_cursor() const {
+
+ int prev_cc = cursor.column;
+ while(prev_cc >0) {
+ bool is_char = _is_text_char(text[cursor.line][prev_cc-1]);
+ if (!is_char)
+ break;
+ --prev_cc;
+ }
+
+ int next_cc = cursor.column;
+ while(next_cc<text[cursor.line].length()) {
+ bool is_char = _is_text_char(text[cursor.line][next_cc]);
+ if(!is_char)
+ break;
+ ++ next_cc;
+ }
+ if (prev_cc == cursor.column || next_cc == cursor.column)
+ return "";
+ return text[cursor.line].substr(prev_cc, next_cc-prev_cc);
+}
DVector<int> TextEdit::_search_bind(const String &p_key,uint32_t p_search_flags, int p_from_line,int p_from_column) const {
@@ -2496,7 +2742,7 @@ void TextEdit::_clear_redo() {
void TextEdit::undo() {
-
+
_push_current_op();
if (undo_stack_pos==NULL) {
@@ -2511,8 +2757,13 @@ void TextEdit::undo() {
else
undo_stack_pos=undo_stack_pos->prev();
-
_do_text_op( undo_stack_pos->get(),true);
+ if(undo_stack_pos->get().chain_backward) {
+ do {
+ undo_stack_pos = undo_stack_pos->prev();
+ _do_text_op(undo_stack_pos->get(), true);
+ } while(!undo_stack_pos->get().chain_forward);
+ }
cursor_set_line(undo_stack_pos->get().from_line);
cursor_set_column(undo_stack_pos->get().from_column);
@@ -2526,8 +2777,13 @@ void TextEdit::redo() {
if (undo_stack_pos==NULL)
return; //nothing to do.
-
- _do_text_op( undo_stack_pos->get(),false);
+ _do_text_op(undo_stack_pos->get(), false);
+ if(undo_stack_pos->get().chain_forward) {
+ do {
+ undo_stack_pos=undo_stack_pos->next();
+ _do_text_op(undo_stack_pos->get(), false);
+ } while(!undo_stack_pos->get().chain_backward);
+ }
cursor_set_line(undo_stack_pos->get().from_line);
cursor_set_column(undo_stack_pos->get().from_column);
undo_stack_pos=undo_stack_pos->next();
@@ -2539,20 +2795,43 @@ void TextEdit::clear_undo_history() {
saved_version=0;
current_op.type=TextOperation::TYPE_NONE;
undo_stack_pos=NULL;
- undo_stack.clear();;
+ undo_stack.clear();
}
+void TextEdit::_begin_compex_operation() {
+ _push_current_op();
+ next_operation_is_complex=true;
+}
-void TextEdit::_push_current_op() {
+void TextEdit::_end_compex_operation() {
+
+ _push_current_op();
+ ERR_FAIL_COND(undo_stack.size() == 0);
+
+ if(undo_stack.back()->get().chain_forward) {
+ undo_stack.back()->get().chain_forward=false;
+ return;
+ }
+
+ undo_stack.back()->get().chain_backward=true;
+}
+void TextEdit::_push_current_op() {
+
if (current_op.type==TextOperation::TYPE_NONE)
return; // do nothing
-
+
+ if(next_operation_is_complex) {
+ current_op.chain_forward=true;
+ next_operation_is_complex=false;
+ }
+
undo_stack.push_back(current_op);
current_op.type=TextOperation::TYPE_NONE;
current_op.text="";
-
+ current_op.chain_forward=false;
+
}
void TextEdit::set_draw_tabs(bool p_draw) {
@@ -2693,6 +2972,7 @@ void TextEdit::_update_completion_candidates() {
completion_current=completion_options[completion_index];
+#if 0 // even there's only one option, user still get the chance to choose using it or not
if (completion_options.size()==1) {
//one option to complete, just complete it automagically
_confirm_completion();
@@ -2701,6 +2981,9 @@ void TextEdit::_update_completion_candidates() {
return;
}
+#endif
+ if (completion_options.size()==1 && s==completion_options[0])
+ _cancel_completion();
completion_enabled=true;
@@ -2855,6 +3138,7 @@ void TextEdit::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_selection_to_line"),&TextEdit::get_selection_to_line);
ObjectTypeDB::bind_method(_MD("get_selection_to_column"),&TextEdit::get_selection_to_column);
ObjectTypeDB::bind_method(_MD("get_selection_text"),&TextEdit::get_selection_text);
+ ObjectTypeDB::bind_method(_MD("get_word_under_cursor"),&TextEdit::get_word_under_cursor);
ObjectTypeDB::bind_method(_MD("search","flags","from_line","from_column","to_line","to_column"),&TextEdit::_search_bind);
ObjectTypeDB::bind_method(_MD("undo"),&TextEdit::undo);
@@ -2945,7 +3229,7 @@ TextEdit::TextEdit() {
current_op.type=TextOperation::TYPE_NONE;
undo_enabled=true;
- undo_stack_pos=NULL;
+ undo_stack_pos=NULL;
setting_text=false;
last_dblclk=0;
current_op.version=0;
@@ -2957,6 +3241,8 @@ TextEdit::TextEdit() {
completion_line_ofs=0;
tooltip_obj=NULL;
line_numbers=false;
+ next_operation_is_complex=false;
+ auto_brace_completion_enabled=false;
}
TextEdit::~TextEdit(){