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.cpp271
1 files changed, 170 insertions, 101 deletions
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 07f1bdf8e5..87043c65eb 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,14 +42,14 @@
#define TAB_PIXELS
-static bool _is_text_char(CharType c) {
+inline bool _is_symbol(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return is_symbol(c);
}
-static bool _is_symbol(CharType c) {
+static bool _is_text_char(CharType c) {
- return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
static bool _is_whitespace(CharType c) {
@@ -1498,7 +1498,7 @@ void TextEdit::_notification(int p_what) {
if (OS::get_singleton()->has_virtual_keyboard())
OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect());
if (raised_from_completion) {
- VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1);
+ VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
}
} break;
@@ -1512,7 +1512,7 @@ void TextEdit::_notification(int p_what) {
if (OS::get_singleton()->has_virtual_keyboard())
OS::get_singleton()->hide_virtual_keyboard();
if (raised_from_completion) {
- VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0);
+ VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0);
}
} break;
}
@@ -1695,10 +1695,9 @@ void TextEdit::indent_right() {
// fix selection and cursor being off by one on the last line
if (is_selection_active()) {
- selection.to_column++;
- selection.from_column++;
+ select(selection.from_line, selection.from_column + 1, selection.to_line, selection.to_column + 1);
}
- cursor.column++;
+ cursor_set_column(cursor.column + 1, false);
end_complex_operation();
update();
}
@@ -1737,14 +1736,9 @@ void TextEdit::indent_left() {
// fix selection and cursor being off by one on the last line
if (is_selection_active() && last_line_text != get_line(end_line)) {
- if (selection.to_column > 0)
- selection.to_column--;
- if (selection.from_column > 0)
- selection.from_column--;
- }
- if (cursor.column > 0) {
- cursor.column--;
+ select(selection.from_line, selection.from_column - 1, selection.to_line, selection.to_column - 1);
}
+ cursor_set_column(cursor.column - 1, false);
end_complex_operation();
update();
}
@@ -1828,10 +1822,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->is_pressed()) {
if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) {
- _scroll_up(3 * mb->get_factor());
+ if (mb->get_shift()) {
+ h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
+ } else {
+ _scroll_up(3 * mb->get_factor());
+ }
}
if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) {
- _scroll_down(3 * mb->get_factor());
+ if (mb->get_shift()) {
+ h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
+ } else {
+ _scroll_down(3 * mb->get_factor());
+ }
}
if (mb->get_button_index() == BUTTON_WHEEL_LEFT) {
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
@@ -1962,7 +1964,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else if (mb->is_doubleclick() && text[cursor.line].length()) {
- //doubleclick select world
+ //doubleclick select word
selection.selecting_mode = Selection::MODE_WORD;
_update_selection_mode_word();
last_dblclk = OS::get_singleton()->get_ticks_msec();
@@ -1973,6 +1975,31 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
+ _reset_caret_blink_timer();
+
+ int row, col;
+ update_line_scroll_pos();
+ _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
+
+ if (is_right_click_moving_caret()) {
+ if (is_selection_active()) {
+
+ int from_line = get_selection_from_line();
+ int to_line = get_selection_to_line();
+ int from_column = get_selection_from_column();
+ int to_column = get_selection_to_column();
+
+ if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
+ // Right click is outside the seleted text
+ deselect();
+ }
+ }
+ if (!is_selection_active()) {
+ cursor_set_line(row, true, false);
+ cursor_set_column(col);
+ }
+ }
+
menu->set_position(get_global_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
menu->popup();
@@ -2420,26 +2447,44 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
//simple unindent
int cc = cursor.column;
+
+ const int len = text[cursor.line].length();
+ const String &line = text[cursor.line];
+
+ int left = 0; // number of whitespace chars at beginning of line
+ while (left < len && (line[left] == '\t' || line[left] == ' '))
+ left++;
+ cc = MIN(cc, left);
+
+ while (cc < indent_size && cc < left && line[cc] == ' ')
+ cc++;
+
if (cc > 0 && cc <= text[cursor.line].length()) {
- if (text[cursor.line][cursor.column - 1] == '\t') {
- backspace_at_cursor();
+ if (text[cursor.line][cc - 1] == '\t') {
+ _remove_text(cursor.line, cc - 1, cursor.line, cc);
+ if (cursor.column >= left)
+ cursor_set_column(MAX(0, cursor.column - 1));
+ update();
} else {
- if (cursor.column - indent_size >= 0) {
+ int n = 0;
- bool unindent = true;
- for (int i = 1; i <= indent_size; i++) {
- if (text[cursor.line][cursor.column - i] != ' ') {
- unindent = false;
- break;
- }
+ for (int i = 1; i <= MIN(cc, indent_size); i++) {
+ if (line[cc - i] != ' ') {
+ break;
}
+ n++;
+ }
- if (unindent) {
- _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column);
- cursor_set_column(cursor.column - indent_size);
- }
+ if (n > 0) {
+ _remove_text(cursor.line, cc - n, cursor.line, cc);
+ if (cursor.column > left - n) // inside text?
+ cursor_set_column(MAX(0, cursor.column - n));
+ update();
}
}
+ } else if (cc == 0 && line.length() > 0 && line[0] == '\t') {
+ _remove_text(cursor.line, 0, cursor.line, 1);
+ update();
}
} else {
//simple indent
@@ -2544,22 +2589,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (cc == 0 && cursor.line > 0) {
cursor_set_line(cursor.line - 1);
cursor_set_column(text[cursor.line].length());
- break;
- }
-
- while (cc > 0) {
-
- bool ischar = _is_text_char(text[cursor.line][cc - 1]);
+ } else {
+ while (cc > 0) {
+ bool ischar = _is_text_char(text[cursor.line][cc - 1]);
- if (prev_char && !ischar)
- break;
+ if (prev_char && !ischar)
+ break;
- prev_char = ischar;
- cc--;
+ prev_char = ischar;
+ cc--;
+ }
+ cursor_set_column(cc);
}
- cursor_set_column(cc);
-
} else if (cursor.column == 0) {
if (cursor.line > 0) {
@@ -2608,21 +2650,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) {
cursor_set_line(cursor.line + 1);
cursor_set_column(0);
- break;
- }
-
- while (cc < text[cursor.line].length()) {
-
- bool ischar = _is_text_char(text[cursor.line][cc]);
+ } else {
+ while (cc < text[cursor.line].length()) {
+ bool ischar = _is_text_char(text[cursor.line][cc]);
- if (prev_char && !ischar)
- break;
- prev_char = ischar;
- cc++;
+ if (prev_char && !ischar)
+ break;
+ prev_char = ischar;
+ cc++;
+ }
+ cursor_set_column(cc);
}
- cursor_set_column(cc);
-
} else if (cursor.column == text[cursor.line].length()) {
if (cursor.line < text.size() - 1) {
@@ -2694,6 +2733,8 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_scroll_lines_down();
break;
}
+
+ {
#else
if (k->get_command() && k->get_alt()) {
_scroll_lines_down();
@@ -2702,9 +2743,15 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_command())
cursor_set_line(text.size() - 1, true, false);
- else
+ else {
#endif
- cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false);
+ if (!is_last_visible_line(cursor.line)) {
+ cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false);
+ } else {
+ cursor_set_line(text.size() - 1);
+ cursor_set_column(get_line(cursor.line).length(), true);
+ }
+ }
if (k->get_shift())
_post_shift_selection();
@@ -3100,6 +3147,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
void TextEdit::_scroll_up(real_t p_delta) {
+ if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta))
+ scrolling = false;
+
if (scrolling) {
target_v_scroll = (target_v_scroll - p_delta);
} else {
@@ -3110,8 +3160,12 @@ void TextEdit::_scroll_up(real_t p_delta) {
if (target_v_scroll <= 0) {
target_v_scroll = 0;
}
- scrolling = true;
- set_physics_process(true);
+ if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) {
+ v_scroll->set_value(target_v_scroll);
+ } else {
+ scrolling = true;
+ set_physics_process(true);
+ }
} else {
v_scroll->set_value(target_v_scroll);
}
@@ -3119,6 +3173,9 @@ void TextEdit::_scroll_up(real_t p_delta) {
void TextEdit::_scroll_down(real_t p_delta) {
+ if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta))
+ scrolling = false;
+
if (scrolling) {
target_v_scroll = (target_v_scroll + p_delta);
} else {
@@ -3135,8 +3192,13 @@ void TextEdit::_scroll_down(real_t p_delta) {
if (target_v_scroll > max_v_scroll) {
target_v_scroll = max_v_scroll;
}
- scrolling = true;
- set_physics_process(true);
+
+ if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) {
+ v_scroll->set_value(target_v_scroll);
+ } else {
+ scrolling = true;
+ set_physics_process(true);
+ }
} else {
v_scroll->set_value(target_v_scroll);
}
@@ -3708,6 +3770,14 @@ bool TextEdit::cursor_is_block_mode() const {
return block_caret;
}
+void TextEdit::set_right_click_moves_caret(bool p_enable) {
+ right_click_moves_caret = p_enable;
+}
+
+bool TextEdit::is_right_click_moving_caret() const {
+ return right_click_moves_caret;
+}
+
void TextEdit::_v_scroll_input() {
scrolling = false;
}
@@ -4168,11 +4238,15 @@ void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_t
p_from_line = text.size() - 1;
if (p_from_column >= text[p_from_line].length())
p_from_column = text[p_from_line].length();
+ if (p_from_column < 0)
+ p_from_column = 0;
if (p_to_line >= text.size())
p_to_line = text.size() - 1;
if (p_to_column >= text[p_to_line].length())
p_to_column = text[p_to_line].length();
+ if (p_to_column < 0)
+ p_to_column = 0;
selection.from_line = p_from_line;
selection.from_column = p_from_column;
@@ -4541,6 +4615,24 @@ int TextEdit::num_lines_from(int p_line_from, int unhidden_amount) const {
return num_total;
}
+bool TextEdit::is_last_visible_line(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+
+ if (p_line == text.size() - 1)
+ return true;
+
+ if (!is_hiding_enabled())
+ return false;
+
+ for (int i = p_line + 1; i < text.size(); i++) {
+ if (!is_line_hidden(i))
+ return false;
+ }
+
+ return true;
+}
+
int TextEdit::get_indent_level(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
@@ -4970,7 +5062,7 @@ void TextEdit::_confirm_completion() {
void TextEdit::_cancel_code_hint() {
- VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0);
+ VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0);
raised_from_completion = false;
completion_hint = "";
update();
@@ -4978,7 +5070,7 @@ void TextEdit::_cancel_code_hint() {
void TextEdit::_cancel_completion() {
- VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0);
+ VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0);
raised_from_completion = false;
if (!completion_active)
return;
@@ -5153,7 +5245,7 @@ void TextEdit::query_code_comple() {
void TextEdit::set_code_hint(const String &p_hint) {
- VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1);
+ VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
raised_from_completion = true;
completion_hint = p_hint;
completion_hint_offset = -0xFFFF;
@@ -5162,7 +5254,7 @@ void TextEdit::set_code_hint(const String &p_hint) {
void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) {
- VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1);
+ VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
raised_from_completion = true;
completion_strings = p_strings;
completion_active = true;
@@ -5181,12 +5273,8 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
String s = text[row];
if (s.length() == 0)
return "";
- int beg = CLAMP(col, 0, s.length());
- int end = beg;
-
- if (s[beg] > 32 || beg == s.length()) {
-
- bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
+ int beg, end;
+ if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
int qbegin = 0, qend = 0;
@@ -5205,16 +5293,6 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
}
}
- while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
- beg--;
- }
- while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
- end++;
- }
-
- if (end < s.length())
- end += 1;
-
return s.substr(beg, end - beg);
}
@@ -5231,22 +5309,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
String s = text[row];
if (s.length() == 0)
return Control::get_tooltip(p_pos);
- int beg = CLAMP(col, 0, s.length());
- int end = beg;
-
- if (s[beg] > 32 || beg == s.length()) {
-
- bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
-
- while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
- beg--;
- }
- while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
- end++;
- }
-
- if (end < s.length())
- end += 1;
+ int beg, end;
+ if (select_word(s, col, beg, end)) {
String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
@@ -5457,6 +5521,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("cursor_set_block_mode", "enable"), &TextEdit::cursor_set_block_mode);
ClassDB::bind_method(D_METHOD("cursor_is_block_mode"), &TextEdit::cursor_is_block_mode);
+ ClassDB::bind_method(D_METHOD("set_right_click_moves_caret", "enable"), &TextEdit::set_right_click_moves_caret);
+ ClassDB::bind_method(D_METHOD("is_right_click_moving_caret"), &TextEdit::is_right_click_moving_caret);
+
ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly);
ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly);
@@ -5492,7 +5559,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
ClassDB::bind_method(D_METHOD("set_line_as_hidden", "line", "enable"), &TextEdit::set_line_as_hidden);
- ClassDB::bind_method(D_METHOD("is_line_hidden"), &TextEdit::is_line_hidden);
+ ClassDB::bind_method(D_METHOD("is_line_hidden", "line"), &TextEdit::is_line_hidden);
ClassDB::bind_method(D_METHOD("fold_all_lines"), &TextEdit::fold_all_lines);
ClassDB::bind_method(D_METHOD("unhide_all_lines"), &TextEdit::unhide_all_lines);
ClassDB::bind_method(D_METHOD("fold_line", "line"), &TextEdit::fold_line);
@@ -5540,6 +5607,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret");
ADD_SIGNAL(MethodInfo("cursor_changed"));
ADD_SIGNAL(MethodInfo("text_changed"));
@@ -5617,6 +5685,7 @@ TextEdit::TextEdit() {
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", this, "_toggle_draw_caret");
cursor_set_blink_enabled(false);
+ right_click_moves_caret = true;
idle_detect = memnew(Timer);
add_child(idle_detect);