diff options
Diffstat (limited to 'scene/gui/text_edit.cpp')
-rw-r--r-- | scene/gui/text_edit.cpp | 186 |
1 files changed, 137 insertions, 49 deletions
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 8ddc31745e..2fd1ba5535 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-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 */ @@ -417,7 +417,6 @@ void TextEdit::_update_scrollbars() { cursor.line_ofs = 0; cursor.wrap_ofs = 0; v_scroll->set_value(0); - v_scroll->set_max(0); v_scroll->hide(); } @@ -436,7 +435,6 @@ void TextEdit::_update_scrollbars() { cursor.x_ofs = 0; h_scroll->set_value(0); - h_scroll->set_max(0); h_scroll->hide(); } @@ -1218,7 +1216,7 @@ void TextEdit::_notification(int p_what) { int horizontal_gap = (cache.info_gutter_width * 30) / 100; int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width; - Ref<Texture> info_icon = text.get_info_icon(line); + Ref<Texture2D> info_icon = text.get_info_icon(line); // Ensure the icon fits the gutter size. Size2i icon_size = info_icon->get_size(); if (icon_size.width > cache.info_gutter_width - horizontal_gap) { @@ -1647,7 +1645,7 @@ void TextEdit::_notification(int p_what) { Point2 title_pos(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent() + yofs); // Draw completion icon if it is valid. - Ref<Texture> icon = completion_options[l].icon; + Ref<Texture2D> icon = completion_options[l].icon; Rect2 icon_area(completion_rect.position.x, completion_rect.position.y + i * get_row_height(), icon_area_size.width, icon_area_size.height); if (icon.is_valid()) { const real_t max_scale = 0.7f; @@ -1846,6 +1844,42 @@ void TextEdit::_consume_pair_symbol(CharType ch) { } } + String line = text[cursor.line]; + + bool in_single_quote = false; + bool in_double_quote = false; + + int c = 0; + while (c < line.length()) { + if (line[c] == '\\') { + c++; // Skip quoted anything. + + if (cursor.column == c) { + break; + } + } else { + if (line[c] == '\'' && !in_double_quote) { + in_single_quote = !in_single_quote; + } else if (line[c] == '"' && !in_single_quote) { + in_double_quote = !in_double_quote; + } + } + + c++; + + if (cursor.column == c) { + break; + } + } + + // Disallow inserting duplicated quotes while already in string + if ((in_single_quote || in_double_quote) && (ch == '"' || ch == '\'')) { + insert_text_at_cursor(ch_single); + cursor_set_column(cursor_position_to_move); + + return; + } + insert_text_at_cursor(ch_pair); cursor_set_column(cursor_position_to_move); } @@ -1948,6 +1982,7 @@ void TextEdit::indent_right() { // Ignore if the cursor is not past the first column. if (is_selection_active() && get_selection_to_column() == 0) { + selection_offset = 0; end_line--; } @@ -3069,7 +3104,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_LEFT: { @@ -3145,7 +3180,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_RIGHT: { @@ -3206,7 +3241,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_UP: { @@ -3259,7 +3294,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_DOWN: { @@ -3382,7 +3417,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_HOME: { #ifdef APPLE_STYLE_KEYS @@ -3443,7 +3478,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_END: { #ifdef APPLE_STYLE_KEYS @@ -3490,7 +3525,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_PAGEUP: { @@ -3513,7 +3548,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - FALLTHROUGH; + [[fallthrough]]; } case KEY_PAGEDOWN: { @@ -4440,7 +4475,6 @@ int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const { } void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { - if (p_col < 0) p_col = 0; @@ -4482,7 +4516,7 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_ if (p_row - move_up > 0 && !is_line_hidden(p_row - move_up)) { p_row -= move_up; } else { - WARN_PRINTS(("Cursor set to hidden line " + itos(p_row) + " and there are no nonhidden lines.")); + WARN_PRINT(("Cursor set to hidden line " + itos(p_row) + " and there are no nonhidden lines.")); } } } @@ -4593,6 +4627,7 @@ void TextEdit::_scroll_moved(double p_to_val) { break; } } + n_line = MIN(n_line, text.size() - 1); int line_wrap_amount = times_line_wraps(n_line); int wi = line_wrap_amount - (sc - v_scroll_i - 1); wi = CLAMP(wi, 0, line_wrap_amount); @@ -5179,11 +5214,16 @@ void TextEdit::cut() { 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()); - backspace_at_cursor(); + if (cursor.line == 0 && get_line_count() > 1) { + _remove_text(cursor.line, 0, cursor.line + 1, 0); + } else { + _remove_text(cursor.line, 0, cursor.line, text[cursor.line].length()); + backspace_at_cursor(); + cursor_set_line(cursor.line + 1); + } + update(); - cursor_set_line(cursor.line + 1); cut_copy_line = clipboard; } else { @@ -5442,11 +5482,11 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc return col; } -PoolVector<int> TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const { +Vector<int> TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const { int col, line; if (search(p_key, p_search_flags, p_from_line, p_from_column, line, col)) { - PoolVector<int> result; + Vector<int> result; result.resize(2); result.set(SEARCH_RESULT_COLUMN, col); result.set(SEARCH_RESULT_LINE, line); @@ -5454,7 +5494,7 @@ PoolVector<int> TextEdit::_search_bind(const String &p_key, uint32_t p_search_fl } else { - return PoolVector<int>(); + return Vector<int>(); } } @@ -5686,7 +5726,7 @@ void TextEdit::remove_breakpoints() { } } -void TextEdit::set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info) { +void TextEdit::set_line_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info) { ERR_FAIL_INDEX(p_line, text.size()); text.set_info_icon(p_line, p_icon, p_info); update(); @@ -6526,6 +6566,10 @@ void TextEdit::_update_completion_candidates() { Vector<float> sim_cache; bool single_quote = s.begins_with("'"); Vector<ScriptCodeCompletionOption> completion_options_casei; + Vector<ScriptCodeCompletionOption> completion_options_subseq; + Vector<ScriptCodeCompletionOption> completion_options_subseq_casei; + + String s_lower = s.to_lower(); for (List<ScriptCodeCompletionOption>::Element *E = completion_sources.front(); E; E = E->next()) { ScriptCodeCompletionOption &option = E->get(); @@ -6540,31 +6584,68 @@ void TextEdit::_update_completion_candidates() { option.insert_text = option.insert_text.quote(quote); } - if (option.display.begins_with(s)) { + if (option.display.length() == 0) { + continue; + } else if (s.length() == 0) { completion_options.push_back(option); - } else if (option.display.to_lower().begins_with(s.to_lower())) { - completion_options_casei.push_back(option); - } - } + } else { - completion_options.append_array(completion_options_casei); + // This code works the same as: + /* + if (option.display.begins_with(s)) { + completion_options.push_back(option); + } else if (option.display.to_lower().begins_with(s.to_lower())) { + completion_options_casei.push_back(option); + } else if (s.is_subsequence_of(option.display)) { + completion_options_subseq.push_back(option); + } else if (s.is_subsequence_ofi(option.display)) { + completion_options_subseq_casei.push_back(option); + } + */ + // But is more performant due to being inlined and looping over the characters only once - if (completion_options.size() == 0) { - for (int i = 0; i < completion_sources.size(); i++) { - if (s.is_subsequence_of(completion_sources[i].display)) { - completion_options.push_back(completion_sources[i]); + String display_lower = option.display.to_lower(); + + const CharType *ssq = &s[0]; + const CharType *ssq_lower = &s_lower[0]; + + const CharType *tgt = &option.display[0]; + const CharType *tgt_lower = &display_lower[0]; + + const CharType *ssq_last_tgt = NULL; + const CharType *ssq_lower_last_tgt = NULL; + + for (; *tgt; tgt++, tgt_lower++) { + if (*ssq == *tgt) { + ssq++; + ssq_last_tgt = tgt; + } + if (*ssq_lower == *tgt_lower) { + ssq_lower++; + ssq_lower_last_tgt = tgt; + } } - } - } - if (completion_options.size() == 0) { - for (int i = 0; i < completion_sources.size(); i++) { - if (s.is_subsequence_ofi(completion_sources[i].display)) { - completion_options.push_back(completion_sources[i]); + if (!*ssq) { // Matched the whole subsequence in s + if (ssq_last_tgt == &option.display[s.length() - 1]) { // Finished matching in the first s.length() characters + completion_options.push_back(option); + } else { + completion_options_subseq.push_back(option); + } + } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower + if (ssq_lower_last_tgt == &option.display[s.length() - 1]) { // Finished matching in the first s.length() characters + completion_options_casei.push_back(option); + } else { + completion_options_subseq_casei.push_back(option); + } } } } + completion_options.append_array(completion_options_casei); + completion_options.append_array(completion_options_subseq); + completion_options.append_array(completion_options_subseq_casei); + if (completion_options.size() == 0) { // No options to complete, cancel. _cancel_completion(); @@ -7066,6 +7147,10 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled); ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); + ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &TextEdit::set_v_scroll); + ClassDB::bind_method(D_METHOD("get_v_scroll"), &TextEdit::get_v_scroll); + ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &TextEdit::set_h_scroll); + ClassDB::bind_method(D_METHOD("get_h_scroll"), &TextEdit::get_h_scroll); ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color); ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color); @@ -7098,9 +7183,11 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); ADD_GROUP("Minimap", "minimap_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap"); @@ -7109,7 +7196,7 @@ void TextEdit::_bind_methods() { ADD_GROUP("Caret", "caret_"); 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_PROPERTY(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "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")); @@ -7129,7 +7216,7 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_MAX); GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3); - ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::REAL, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers. + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::FLOAT, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers. } TextEdit::TextEdit() { @@ -7142,6 +7229,7 @@ TextEdit::TextEdit() { max_chars = 0; clear(); wrap_enabled = false; + wrap_at = 0; wrap_right_offset = 10; set_focus_mode(FOCUS_ALL); syntax_highlighter = NULL; @@ -7171,10 +7259,10 @@ TextEdit::TextEdit() { updating_scrolls = false; selection.active = false; - h_scroll->connect("value_changed", this, "_scroll_moved"); - v_scroll->connect("value_changed", this, "_scroll_moved"); + h_scroll->connect_compat("value_changed", this, "_scroll_moved"); + v_scroll->connect_compat("value_changed", this, "_scroll_moved"); - v_scroll->connect("scrolling", this, "_v_scroll_input"); + v_scroll->connect_compat("scrolling", this, "_v_scroll_input"); cursor_changed_dirty = false; text_changed_dirty = false; @@ -7191,7 +7279,7 @@ TextEdit::TextEdit() { caret_blink_timer = memnew(Timer); add_child(caret_blink_timer); caret_blink_timer->set_wait_time(0.65); - caret_blink_timer->connect("timeout", this, "_toggle_draw_caret"); + caret_blink_timer->connect_compat("timeout", this, "_toggle_draw_caret"); cursor_set_blink_enabled(false); right_click_moves_caret = true; @@ -7199,12 +7287,12 @@ TextEdit::TextEdit() { add_child(idle_detect); idle_detect->set_one_shot(true); idle_detect->set_wait_time(GLOBAL_GET("gui/timers/text_edit_idle_detect_sec")); - idle_detect->connect("timeout", this, "_push_current_op"); + idle_detect->connect_compat("timeout", this, "_push_current_op"); click_select_held = memnew(Timer); add_child(click_select_held); click_select_held->set_wait_time(0.05); - click_select_held->connect("timeout", this, "_click_selection_held"); + click_select_held->connect_compat("timeout", this, "_click_selection_held"); current_op.type = TextOperation::TYPE_NONE; undo_enabled = true; @@ -7262,7 +7350,7 @@ TextEdit::TextEdit() { add_child(menu); readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly. set_readonly(false); - menu->connect("id_pressed", this, "menu_option"); + menu->connect_compat("id_pressed", this, "menu_option"); first_draw = true; executing_line = -1; |