From 9ec3e7f3d7ae7f9eaf5a1de29346c5e10b3af6f9 Mon Sep 17 00:00:00 2001 From: Paulb23 Date: Fri, 9 Jul 2021 12:42:55 +0100 Subject: [PATCH] Cleanup TextEdit selection methods --- doc/classes/TextEdit.xml | 20 +- editor/code_editor.cpp | 36 +- editor/plugins/script_text_editor.cpp | 20 +- editor/plugins/shader_editor_plugin.cpp | 8 +- editor/plugins/text_editor.cpp | 12 +- scene/gui/code_edit.cpp | 20 +- scene/gui/text_edit.cpp | 795 ++++++++++++------------ scene/gui/text_edit.h | 90 +-- 8 files changed, 516 insertions(+), 485 deletions(-) diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index ce25c8c7f47..088072b411a 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -324,6 +324,13 @@ Returns a [String] text with the word under the caret location. + + + + + Returns [code]true[/code] if the user is has a selection. + + @@ -384,12 +391,6 @@ Returns if the given line is wrapped. - - - - Returns [code]true[/code] if the selection is active. - - @@ -469,6 +470,13 @@ If [member selecting_enabled] is [code]false[/code], no selection will occur. + + + + + Selects the word under the caret. + + diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 57c69f5aab0..5f6b7e17885 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -179,7 +179,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col) } void FindReplaceBar::_replace() { - bool selection_enabled = text_editor->is_selection_active(); + bool selection_enabled = text_editor->has_selection(); Point2i selection_begin, selection_end; if (selection_enabled) { selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column()); @@ -229,7 +229,7 @@ void FindReplaceBar::_replace_all() { Point2i orig_cursor(text_editor->get_caret_line(), text_editor->get_caret_column()); Point2i prev_match = Point2(-1, -1); - bool selection_enabled = text_editor->is_selection_active(); + bool selection_enabled = text_editor->has_selection(); Point2i selection_begin, selection_end; if (selection_enabled) { selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column()); @@ -316,7 +316,7 @@ void FindReplaceBar::_get_search_from(int &r_line, int &r_col) { r_line = text_editor->get_caret_line(); r_col = text_editor->get_caret_column(); - if (text_editor->is_selection_active() && is_selection_only()) { + if (text_editor->has_selection() && is_selection_only()) { return; } @@ -409,7 +409,7 @@ bool FindReplaceBar::search_prev() { int line, col; _get_search_from(line, col); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { col--; // Skip currently selected word. } @@ -487,8 +487,8 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) { search_text->call_deferred(SNAME("grab_focus")); } - if (text_editor->is_selection_active() && !selection_only->is_pressed()) { - search_text->set_text(text_editor->get_selection_text()); + if (text_editor->has_selection() && !selection_only->is_pressed()) { + search_text->set_text(text_editor->get_selected_text()); } if (!get_search_text().is_empty()) { @@ -521,9 +521,9 @@ void FindReplaceBar::popup_replace() { hbc_option_replace->show(); } - selection_only->set_pressed((text_editor->is_selection_active() && text_editor->get_selection_from_line() < text_editor->get_selection_to_line())); + selection_only->set_pressed((text_editor->has_selection() && text_editor->get_selection_from_line() < text_editor->get_selection_to_line())); - _show_search(is_visible() || text_editor->is_selection_active()); + _show_search(is_visible() || text_editor->has_selection()); } void FindReplaceBar::_search_options_changed(bool p_pressed) { @@ -554,7 +554,7 @@ void FindReplaceBar::_search_text_submitted(const String &p_text) { } void FindReplaceBar::_replace_text_submitted(const String &p_text) { - if (selection_only->is_pressed() && text_editor->is_selection_active()) { + if (selection_only->is_pressed() && text_editor->has_selection()) { _replace_all(); _hide_bar(); } else if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { @@ -1125,7 +1125,7 @@ void CodeTextEditor::convert_indent_to_tabs() { } void CodeTextEditor::convert_case(CaseStyle p_case) { - if (!text_editor->is_selection_active()) { + if (!text_editor->has_selection()) { return; } @@ -1171,7 +1171,7 @@ void CodeTextEditor::convert_case(CaseStyle p_case) { void CodeTextEditor::move_lines_up() { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int from_line = text_editor->get_selection_from_line(); int from_col = text_editor->get_selection_from_column(); int to_line = text_editor->get_selection_to_line(); @@ -1217,7 +1217,7 @@ void CodeTextEditor::move_lines_up() { void CodeTextEditor::move_lines_down() { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int from_line = text_editor->get_selection_from_line(); int from_col = text_editor->get_selection_from_column(); int to_line = text_editor->get_selection_to_line(); @@ -1276,7 +1276,7 @@ void CodeTextEditor::_delete_line(int p_line) { void CodeTextEditor::delete_lines() { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int to_line = text_editor->get_selection_to_line(); int from_line = text_editor->get_selection_from_line(); int count = Math::abs(to_line - from_line) + 1; @@ -1304,7 +1304,7 @@ void CodeTextEditor::duplicate_selection() { bool selection_active = false; text_editor->set_caret_column(text_editor->get_line(from_line).length()); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { from_column = text_editor->get_selection_from_column(); to_column = text_editor->get_selection_to_column(); @@ -1312,7 +1312,7 @@ void CodeTextEditor::duplicate_selection() { to_line = text_editor->get_selection_to_line(); cursor_new_line = to_line + text_editor->get_caret_line() - from_line; cursor_new_column = to_column == cursor_column ? 2 * to_column - from_column : to_column; - new_text = text_editor->get_selection_text(); + new_text = text_editor->get_selected_text(); selection_active = true; text_editor->set_caret_line(to_line); @@ -1338,7 +1338,7 @@ void CodeTextEditor::duplicate_selection() { void CodeTextEditor::toggle_inline_comment(const String &delimiter) { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int begin = text_editor->get_selection_from_line(); int end = text_editor->get_selection_to_line(); @@ -1447,8 +1447,8 @@ Variant CodeTextEditor::get_edit_state() { state["column"] = text_editor->get_caret_column(); state["row"] = text_editor->get_caret_line(); - state["selection"] = get_text_editor()->is_selection_active(); - if (get_text_editor()->is_selection_active()) { + state["selection"] = get_text_editor()->has_selection(); + if (get_text_editor()->has_selection()) { state["selection_from_line"] = text_editor->get_selection_from_line(); state["selection_from_column"] = text_editor->get_selection_from_column(); state["selection_to_line"] = text_editor->get_selection_to_line(); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 2faa56e661c..ad25bca9da5 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1080,7 +1080,7 @@ void ScriptTextEditor::_edit_option(int p_op) { tx->begin_complex_operation(); int begin, end; - if (tx->is_selection_active()) { + if (tx->has_selection()) { begin = tx->get_selection_from_line(); end = tx->get_selection_to_line(); // ignore if the cursor is not past the first column @@ -1122,7 +1122,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_EVALUATE: { Expression expression; - Vector lines = code_editor->get_text_editor()->get_selection_text().split("\n"); + Vector lines = code_editor->get_text_editor()->get_selected_text().split("\n"); PackedStringArray results; for (int i = 0; i < lines.size(); i++) { @@ -1158,14 +1158,14 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->get_find_replace_bar()->popup_replace(); } break; case SEARCH_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); // Yep, because it doesn't make sense to instance this dialog for every single script open... // So this will be delegated to the ScriptEditor. emit_signal(SNAME("search_in_files_requested"), selected_text); } break; case REPLACE_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); emit_signal(SNAME("replace_in_files_requested"), selected_text); } break; @@ -1256,7 +1256,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case HELP_CONTEXTUAL: { - String text = tx->get_selection_text(); + String text = tx->get_selected_text(); if (text == "") { text = tx->get_word_under_caret(); } @@ -1267,7 +1267,7 @@ void ScriptTextEditor::_edit_option(int p_op) { case LOOKUP_SYMBOL: { String text = tx->get_word_under_caret(); if (text == "") { - text = tx->get_selection_text(); + text = tx->get_selected_text(); } if (text != "") { _lookup_symbol(text, tx->get_caret_line(), tx->get_caret_column()); @@ -1517,7 +1517,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref &ev) { tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); if (tx->is_move_caret_on_right_click_enabled()) { - if (tx->is_selection_active()) { + if (tx->has_selection()) { int from_line = tx->get_selection_from_line(); int to_line = tx->get_selection_to_line(); int from_column = tx->get_selection_from_column(); @@ -1528,7 +1528,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref &ev) { tx->deselect(); } } - if (!tx->is_selection_active()) { + if (!tx->has_selection()) { tx->set_caret_line(row, false, false); tx->set_caret_column(col); } @@ -1539,7 +1539,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref &ev) { word_at_pos = tx->get_word_under_caret(); } if (word_at_pos == "") { - word_at_pos = tx->get_selection_text(); + word_at_pos = tx->get_selected_text(); } bool has_color = (word_at_pos == "Color"); @@ -1591,7 +1591,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref &ev) { has_color = false; } } - _make_context_menu(tx->is_selection_active(), has_color, foldable, open_docs, goto_definition, local_pos); + _make_context_menu(tx->has_selection(), has_color, foldable, open_docs, goto_definition, local_pos); } } diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 3190a0d8ef8..00890227cb0 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -556,7 +556,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref &ev) { tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); if (tx->is_move_caret_on_right_click_enabled()) { - if (tx->is_selection_active()) { + if (tx->has_selection()) { int from_line = tx->get_selection_from_line(); int to_line = tx->get_selection_to_line(); int from_column = tx->get_selection_from_column(); @@ -567,12 +567,12 @@ void ShaderEditor::_text_edit_gui_input(const Ref &ev) { tx->deselect(); } } - if (!tx->is_selection_active()) { + if (!tx->has_selection()) { tx->set_caret_line(row, true, false); tx->set_caret_column(col); } } - _make_context_menu(tx->is_selection_active(), get_local_mouse_position()); + _make_context_menu(tx->has_selection(), get_local_mouse_position()); } } @@ -580,7 +580,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref &ev) { if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) { CodeEdit *tx = shader_editor->get_text_editor(); tx->adjust_viewport_to_caret(); - _make_context_menu(tx->is_selection_active(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); + _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); context_menu->grab_focus(); } } diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 6eea8ae2271..4e90399433c 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -374,14 +374,14 @@ void TextEditor::_edit_option(int p_op) { code_editor->get_find_replace_bar()->popup_replace(); } break; case SEARCH_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); // Yep, because it doesn't make sense to instance this dialog for every single script open... // So this will be delegated to the ScriptEditor. emit_signal(SNAME("search_in_files_requested"), selected_text); } break; case REPLACE_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); emit_signal(SNAME("replace_in_files_requested"), selected_text); } break; @@ -436,7 +436,7 @@ void TextEditor::_text_edit_gui_input(const Ref &ev) { bool is_folded = tx->is_line_folded(row); if (tx->is_move_caret_on_right_click_enabled()) { - if (tx->is_selection_active()) { + if (tx->has_selection()) { int from_line = tx->get_selection_from_line(); int to_line = tx->get_selection_to_line(); int from_column = tx->get_selection_from_column(); @@ -447,14 +447,14 @@ void TextEditor::_text_edit_gui_input(const Ref &ev) { tx->deselect(); } } - if (!tx->is_selection_active()) { + if (!tx->has_selection()) { tx->set_caret_line(row, true, false); tx->set_caret_column(col); } } if (!mb->is_pressed()) { - _make_context_menu(tx->is_selection_active(), can_fold, is_folded, get_local_mouse_position()); + _make_context_menu(tx->has_selection(), can_fold, is_folded, get_local_mouse_position()); } } } @@ -464,7 +464,7 @@ void TextEditor::_text_edit_gui_input(const Ref &ev) { CodeEdit *tx = code_editor->get_text_editor(); int line = tx->get_caret_line(); tx->adjust_viewport_to_caret(); - _make_context_menu(tx->is_selection_active(), tx->can_fold_line(line), tx->is_line_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); + _make_context_menu(tx->has_selection(), tx->can_fold_line(line), tx->is_line_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); context_menu->grab_focus(); } } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 6528e90f30e..134151dbe87 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -548,7 +548,7 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { // Overridable actions void CodeEdit::_handle_unicode_input(const uint32_t p_unicode) { - bool had_selection = is_selection_active(); + bool had_selection = has_selection(); if (had_selection) { begin_complex_operation(); delete_selection(); @@ -611,7 +611,7 @@ void CodeEdit::_backspace() { return; } - if (is_selection_active()) { + if (has_selection()) { delete_selection(); return; } @@ -718,7 +718,7 @@ void CodeEdit::do_indent() { return; } - if (is_selection_active()) { + if (has_selection()) { indent_lines(); return; } @@ -747,7 +747,7 @@ void CodeEdit::indent_lines() { int start_line = get_caret_line(); int end_line = start_line; - if (is_selection_active()) { + if (has_selection()) { start_line = get_selection_from_line(); end_line = get_selection_to_line(); @@ -760,7 +760,7 @@ void CodeEdit::indent_lines() { for (int i = start_line; i <= end_line; i++) { const String line_text = get_line(i); - if (line_text.size() == 0 && is_selection_active()) { + if (line_text.size() == 0 && has_selection()) { continue; } @@ -777,7 +777,7 @@ void CodeEdit::indent_lines() { } /* Fix selection and caret being off after shifting selection right.*/ - if (is_selection_active()) { + if (has_selection()) { select(start_line, get_selection_from_column() + selection_offset, get_selection_to_line(), get_selection_to_column() + selection_offset); } set_caret_column(get_caret_column() + selection_offset, false); @@ -792,7 +792,7 @@ void CodeEdit::do_unindent() { int cc = get_caret_column(); - if (is_selection_active() || cc <= 0) { + if (has_selection() || cc <= 0) { unindent_lines(); return; } @@ -839,7 +839,7 @@ void CodeEdit::unindent_lines() { int start_line = get_caret_line(); int end_line = start_line; - if (is_selection_active()) { + if (has_selection()) { start_line = get_selection_from_line(); end_line = get_selection_to_line(); @@ -882,7 +882,7 @@ void CodeEdit::unindent_lines() { } } - if (is_selection_active()) { + if (has_selection()) { /* Fix selection being off by one on the first line. */ if (first_line_edited) { select(get_selection_from_line(), get_selection_from_column() - removed_characters, get_selection_to_line(), initial_selection_end_column); @@ -1423,7 +1423,7 @@ void CodeEdit::fold_line(int p_line) { } /* Fix selection. */ - if (is_selection_active()) { + if (has_selection()) { if (is_line_hidden(get_selection_from_line()) && is_line_hidden(get_selection_to_line())) { deselect(); } else if (is_line_hidden(get_selection_from_line())) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index e77c8188cc0..5cd95fbbf7c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -315,30 +315,6 @@ void TextEdit::_update_scrollbars() { updating_scrolls = false; } -void TextEdit::_click_selection_held() { - // Warning: is_mouse_button_pressed(MOUSE_BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD - // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem. - // I'm unsure if there's an actual fix that doesn't have a ton of side effects. - if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { - switch (selection.selecting_mode) { - case SelectionMode::SELECTION_MODE_POINTER: { - _update_selection_mode_pointer(); - } break; - case SelectionMode::SELECTION_MODE_WORD: { - _update_selection_mode_word(); - } break; - case SelectionMode::SELECTION_MODE_LINE: { - _update_selection_mode_line(); - } break; - default: { - break; - } - } - } else { - click_select_held->stop(); - } -} - Point2 TextEdit::_get_local_mouse_pos() const { Point2 mp = get_local_mouse_position(); if (is_layout_rtl()) { @@ -347,96 +323,6 @@ Point2 TextEdit::_get_local_mouse_pos() const { return mp; } -void TextEdit::_update_selection_mode_pointer() { - dragging_selection = true; - Point2 mp = _get_local_mouse_pos(); - - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - - select(selection.selecting_line, selection.selecting_column, row, col); - - set_caret_line(row, false); - set_caret_column(col); - update(); - - click_select_held->start(); -} - -void TextEdit::_update_selection_mode_word() { - dragging_selection = true; - Point2 mp = _get_local_mouse_pos(); - - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - - String line = text[row]; - int caret_pos = CLAMP(col, 0, line.length()); - int beg = caret_pos; - int end = beg; - Vector words = TS->shaped_text_get_word_breaks(text.get_line_data(row)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].x < caret_pos && words[i].y > caret_pos) { - beg = words[i].x; - end = words[i].y; - break; - } - } - - // Initial selection. - if (!selection.active) { - select(row, beg, row, end); - selection.selecting_column = beg; - selection.selected_word_beg = beg; - selection.selected_word_end = end; - selection.selected_word_origin = beg; - set_caret_line(selection.to_line, false); - set_caret_column(selection.to_column); - } else { - if ((col <= selection.selected_word_origin && row == selection.selecting_line) || row < selection.selecting_line) { - selection.selecting_column = selection.selected_word_end; - select(row, beg, selection.selecting_line, selection.selected_word_end); - set_caret_line(selection.from_line, false); - set_caret_column(selection.from_column); - } else { - selection.selecting_column = selection.selected_word_beg; - select(selection.selecting_line, selection.selected_word_beg, row, end); - set_caret_line(selection.to_line, false); - set_caret_column(selection.to_column); - } - } - - update(); - - click_select_held->start(); -} - -void TextEdit::_update_selection_mode_line() { - dragging_selection = true; - Point2 mp = _get_local_mouse_pos(); - - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - - col = 0; - if (row < selection.selecting_line) { - // Caret is above us. - set_caret_line(row - 1, false); - selection.selecting_column = text[selection.selecting_line].length(); - } else { - // Caret is below us. - set_caret_line(row + 1, false); - selection.selecting_column = 0; - col = text[row].length(); - } - set_caret_column(0); - - select(selection.selecting_line, selection.selecting_column, row, col); - update(); - - click_select_held->start(); -} - void TextEdit::_update_minimap_click() { Point2 mp = _get_local_mouse_pos(); @@ -739,7 +625,7 @@ void TextEdit::_notification(int p_what) { } // Get the highlighted words. - String highlighted_text = get_selection_text(); + String highlighted_text = get_selected_text(); // Check if highlighted words contain only whitespaces (tabs or spaces). bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String(); @@ -1010,9 +896,9 @@ void TextEdit::_notification(int p_what) { if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) { int char_w = cache.font->get_char_size(' ', 0, cache.font_size).width; if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), cache.selection_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, row_height), cache.selection_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, row_height), selection_color); } } } else { @@ -1124,7 +1010,7 @@ void TextEdit::_notification(int p_what) { if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - draw_rect(rect, cache.selection_color, true); + draw_rect(rect, selection_color, true); } } @@ -1192,7 +1078,7 @@ void TextEdit::_notification(int p_what) { } rect.position.y = TS->shaped_text_get_ascent(rid) + cache.font->get_underline_position(cache.font_size); rect.size.y = cache.font->get_underline_thickness(cache.font_size); - draw_rect(rect, cache.font_selected_color); + draw_rect(rect, font_selected_color); } highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1); @@ -1237,7 +1123,7 @@ void TextEdit::_notification(int p_what) { int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column; if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && override_selected_font_color) { - current_color = cache.font_selected_color; + current_color = font_selected_color; } } @@ -1457,7 +1343,7 @@ void TextEdit::_notification(int p_what) { caret_start = full_text.length(); } else { String pre_text = _base_get_text(0, 0, selection.from_line, selection.from_column); - String post_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + String post_text = get_selected_text(); caret_start = pre_text.length(); caret_end = caret_start + post_text.length(); @@ -1794,7 +1680,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { return; } - if (is_selection_active() || (!p_all_to_left && !p_word)) { + if (has_selection() || (!p_all_to_left && !p_word)) { backspace(); return; } @@ -1831,7 +1717,7 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { return; } - if (is_selection_active()) { + if (has_selection()) { delete_selection(); return; } @@ -1877,19 +1763,6 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { update(); } -void TextEdit::delete_selection() { - if (!is_selection_active()) { - return; - } - - selection.active = false; - selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - set_caret_line(selection.from_line, false, false); - set_caret_column(selection.from_column); - update(); -} - void TextEdit::_move_caret_document_start(bool p_select) { if (p_select) { _pre_shift_selection(); @@ -2187,7 +2060,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); if (is_move_caret_on_right_click_enabled()) { - if (is_selection_active()) { + if (has_selection()) { int from_line = get_selection_from_line(); int to_line = get_selection_to_line(); int from_column = get_selection_from_column(); @@ -2198,7 +2071,7 @@ void TextEdit::_gui_input(const Ref &p_gui_input) { deselect(); } } - if (!is_selection_active()) { + if (!has_selection()) { set_caret_line(row, true, false); set_caret_column(col); } @@ -2565,25 +2438,6 @@ void TextEdit::_scroll_down(real_t p_delta) { } } -void TextEdit::_pre_shift_selection() { - if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) { - selection.selecting_line = caret.line; - selection.selecting_column = caret.column; - selection.active = true; - } - - selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; -} - -void TextEdit::_post_shift_selection() { - if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) { - select(selection.selecting_line, selection.selecting_column, caret.line, caret.column); - update(); - } - - selection.selecting_text = true; -} - void TextEdit::_scroll_lines_up() { scrolling = false; minimap_clicked = false; @@ -3022,30 +2876,6 @@ void TextEdit::center_viewport_to_caret() { update(); } -TextEdit::SelectionMode TextEdit::get_selection_mode() const { - return selection.selecting_mode; -} - -void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column) { - selection.selecting_mode = p_mode; - if (p_line >= 0) { - ERR_FAIL_INDEX(p_line, text.size()); - selection.selecting_line = p_line; - } - if (p_column >= 0) { - ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length()); - selection.selecting_column = p_column; - } -} - -int TextEdit::get_selection_line() const { - return selection.selecting_line; -}; - -int TextEdit::get_selection_column() const { - return selection.selecting_column; -}; - void TextEdit::_v_scroll_input() { scrolling = false; minimap_clicked = false; @@ -3374,6 +3204,10 @@ void TextEdit::_update_caches() { caret_color = get_theme_color(SNAME("caret_color")); caret_background_color = get_theme_color(SNAME("caret_background_color")); + /* Selection */ + font_selected_color = get_theme_color(SNAME("font_selected_color")); + selection_color = get_theme_color(SNAME("selection_color")); + cache.style_normal = get_theme_stylebox(SNAME("normal")); cache.style_focus = get_theme_stylebox(SNAME("focus")); cache.style_readonly = get_theme_stylebox(SNAME("read_only")); @@ -3382,9 +3216,7 @@ void TextEdit::_update_caches() { cache.outline_color = get_theme_color(SNAME("font_outline_color")); cache.outline_size = get_theme_constant(SNAME("outline_size")); cache.font_color = get_theme_color(SNAME("font_color")); - cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); cache.font_readonly_color = get_theme_color(SNAME("font_readonly_color")); - cache.selection_color = get_theme_color(SNAME("selection_color")); cache.current_line_color = get_theme_color(SNAME("current_line_color")); cache.code_folding_color = get_theme_color(SNAME("code_folding_color"), SNAME("CodeEdit")); cache.brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color"), SNAME("CodeEdit")); @@ -3621,6 +3453,215 @@ int TextEdit::get_caret_wrap_index() const { return get_line_wrap_index_at_column(caret.line, caret.column); } +/* Selection. */ +void TextEdit::set_selecting_enabled(const bool p_enabled) { + selecting_enabled = p_enabled; + + if (!selecting_enabled) { + deselect(); + } + + _ensure_menu(); +} + +bool TextEdit::is_selecting_enabled() const { + return selecting_enabled; +} + +void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { + override_selected_font_color = p_override_selected_font_color; +} + +bool TextEdit::is_overriding_selected_font_color() const { + return override_selected_font_color; +} + +void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column) { + selection.selecting_mode = p_mode; + if (p_line >= 0) { + ERR_FAIL_INDEX(p_line, text.size()); + selection.selecting_line = p_line; + } + if (p_column >= 0) { + ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length()); + selection.selecting_column = p_column; + } +} + +TextEdit::SelectionMode TextEdit::get_selection_mode() const { + return selection.selecting_mode; +} + +void TextEdit::select_all() { + if (!selecting_enabled) { + return; + } + + if (text.size() == 1 && text[0].length() == 0) { + return; + } + selection.active = true; + selection.from_line = 0; + selection.from_column = 0; + selection.selecting_line = 0; + selection.selecting_column = 0; + selection.to_line = text.size() - 1; + selection.to_column = text[selection.to_line].length(); + selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; + selection.shiftclick_left = true; + set_caret_line(selection.to_line, false); + set_caret_column(selection.to_column, false); + update(); +} + +void TextEdit::select_word_under_caret() { + if (!selecting_enabled) { + return; + } + + if (text.size() == 1 && text[0].length() == 0) { + return; + } + + if (selection.active) { + /* Allow toggling selection by pressing the shortcut a second time. */ + /* This is also usable as a general-purpose "deselect" shortcut after */ + /* selecting anything. */ + deselect(); + return; + } + + int begin = 0; + int end = 0; + const Vector words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].x <= caret.column && words[i].y >= caret.column) { + begin = words[i].x; + end = words[i].y; + break; + } + } + + select(caret.line, begin, caret.line, end); + /* Move the caret to the end of the word for easier editing. */ + set_caret_column(end, false); +} + +void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { + if (!selecting_enabled) { + return; + } + + if (p_from_line < 0) { + p_from_line = 0; + } else if (p_from_line >= text.size()) { + 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 < 0) { + p_to_line = 0; + } else 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; + selection.to_line = p_to_line; + selection.to_column = p_to_column; + + selection.active = true; + + if (selection.from_line == selection.to_line) { + if (selection.from_column == selection.to_column) { + selection.active = false; + + } else if (selection.from_column > selection.to_column) { + selection.shiftclick_left = false; + SWAP(selection.from_column, selection.to_column); + } else { + selection.shiftclick_left = true; + } + } else if (selection.from_line > selection.to_line) { + selection.shiftclick_left = false; + SWAP(selection.from_line, selection.to_line); + SWAP(selection.from_column, selection.to_column); + } else { + selection.shiftclick_left = true; + } + + update(); +} + +bool TextEdit::has_selection() const { + return selection.active; +} + +String TextEdit::get_selected_text() const { + if (!selection.active) { + return ""; + } + + return _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); +} + +int TextEdit::get_selection_line() const { + return selection.selecting_line; +} + +int TextEdit::get_selection_column() const { + return selection.selecting_column; +} + +int TextEdit::get_selection_from_line() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.from_line; +} + +int TextEdit::get_selection_from_column() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.from_column; +} + +int TextEdit::get_selection_to_line() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.to_line; +} + +int TextEdit::get_selection_to_column() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.to_column; +} + +void TextEdit::deselect() { + selection.active = false; + update(); +} + +void TextEdit::delete_selection() { + if (!has_selection()) { + return; + } + + selection.active = false; + selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + set_caret_line(selection.from_line, false, false); + set_caret_column(selection.from_column); + update(); +} + /* line wrapping. */ void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) { if (line_wrapping_mode != p_wrapping_mode) { @@ -3934,123 +3975,6 @@ Color TextEdit::get_line_background_color(int p_line) { return text.get_line_background_color(p_line); } -void TextEdit::select_all() { - if (!selecting_enabled) { - return; - } - - if (text.size() == 1 && text[0].length() == 0) { - return; - } - selection.active = true; - selection.from_line = 0; - selection.from_column = 0; - selection.selecting_line = 0; - selection.selecting_column = 0; - selection.to_line = text.size() - 1; - selection.to_column = text[selection.to_line].length(); - selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; - selection.shiftclick_left = true; - set_caret_line(selection.to_line, false); - set_caret_column(selection.to_column, false); - update(); -} - -void TextEdit::select_word_under_caret() { - if (!selecting_enabled) { - return; - } - - if (text.size() == 1 && text[0].length() == 0) { - return; - } - - if (selection.active) { - // Allow toggling selection by pressing the shortcut a second time. - // This is also usable as a general-purpose "deselect" shortcut after - // selecting anything. - deselect(); - return; - } - - int begin = 0; - int end = 0; - const Vector words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].x <= caret.column && words[i].y >= caret.column) { - begin = words[i].x; - end = words[i].y; - break; - } - } - - select(caret.line, begin, caret.line, end); - // Move the caret to the end of the word for easier editing. - set_caret_column(end, false); -} - -void TextEdit::deselect() { - selection.active = false; - update(); -} - -void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { - if (!selecting_enabled) { - return; - } - - if (p_from_line < 0) { - p_from_line = 0; - } else if (p_from_line >= text.size()) { - 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 < 0) { - p_to_line = 0; - } else 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; - selection.to_line = p_to_line; - selection.to_column = p_to_column; - - selection.active = true; - - if (selection.from_line == selection.to_line) { - if (selection.from_column == selection.to_column) { - selection.active = false; - - } else if (selection.from_column > selection.to_column) { - selection.shiftclick_left = false; - SWAP(selection.from_column, selection.to_column); - } else { - selection.shiftclick_left = true; - } - } else if (selection.from_line > selection.to_line) { - selection.shiftclick_left = false; - SWAP(selection.from_line, selection.to_line); - SWAP(selection.from_column, selection.to_column); - } else { - selection.shiftclick_left = true; - } - - update(); -} - void TextEdit::swap_lines(int line1, int line2) { String tmp = get_line(line1); String tmp2 = get_line(line2); @@ -4058,38 +3982,6 @@ void TextEdit::swap_lines(int line1, int line2) { set_line(line1, tmp2); } -bool TextEdit::is_selection_active() const { - return selection.active; -} - -int TextEdit::get_selection_from_line() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.from_line; -} - -int TextEdit::get_selection_from_column() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.from_column; -} - -int TextEdit::get_selection_to_line() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.to_line; -} - -int TextEdit::get_selection_to_column() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.to_column; -} - -String TextEdit::get_selection_text() const { - if (!selection.active) { - return ""; - } - - return _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); -} - String TextEdit::get_word_under_caret() const { Vector words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); for (int i = 0; i < words.size(); i++) { @@ -4643,14 +4535,6 @@ bool TextEdit::is_drawing_spaces() const { return draw_spaces; } -void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { - override_selected_font_color = p_override_selected_font_color; -} - -bool TextEdit::is_overriding_selected_font_color() const { - return override_selected_font_color; -} - void TextEdit::set_insert_mode(bool p_enabled) { insert_mode = p_enabled; update(); @@ -4860,7 +4744,7 @@ void TextEdit::set_line(int line, String new_text) { if (caret.line == line) { caret.column = MIN(caret.column, new_text.length()); } - if (is_selection_active() && line == selection.to_line && selection.to_column > text[line].length()) { + if (has_selection() && line == selection.to_line && selection.to_column > text[line].length()) { selection.to_column = text[line].length(); } } @@ -4871,7 +4755,7 @@ void TextEdit::insert_at(const String &p_text, int at) { // offset cursor when located after inserted line ++caret.line; } - if (is_selection_active()) { + if (has_selection()) { if (selection.from_line >= at) { // offset selection when located after inserted line ++selection.from_line; @@ -5080,18 +4964,6 @@ void TextEdit::set_virtual_keyboard_enabled(bool p_enable) { virtual_keyboard_enabled = p_enable; } -void TextEdit::set_selecting_enabled(bool p_enabled) { - selecting_enabled = p_enabled; - - if (!selecting_enabled) { - deselect(); - } -} - -bool TextEdit::is_selecting_enabled() const { - return selecting_enabled; -} - bool TextEdit::is_shortcut_keys_enabled() const { return shortcut_keys_enabled; } @@ -5170,12 +5042,6 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS); BIND_ENUM_CONSTANT(SEARCH_BACKWARDS); - BIND_ENUM_CONSTANT(SELECTION_MODE_NONE); - BIND_ENUM_CONSTANT(SELECTION_MODE_SHIFT); - BIND_ENUM_CONSTANT(SELECTION_MODE_POINTER); - BIND_ENUM_CONSTANT(SELECTION_MODE_WORD); - BIND_ENUM_CONSTANT(SELECTION_MODE_LINE); - ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &TextEdit::get_draw_control_chars); ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &TextEdit::set_draw_control_chars); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction); @@ -5207,11 +5073,6 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("center_viewport_to_caret"), &TextEdit::center_viewport_to_caret); - ClassDB::bind_method(D_METHOD("get_selection_mode"), &TextEdit::get_selection_mode); - ClassDB::bind_method(D_METHOD("set_selection_mode", "mode", "line", "column"), &TextEdit::set_selection_mode, DEFVAL(-1), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_selection_line"), &TextEdit::get_selection_line); - ClassDB::bind_method(D_METHOD("get_selection_column"), &TextEdit::get_selection_column); - ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly); ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly); @@ -5221,22 +5082,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled); ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &TextEdit::set_virtual_keyboard_enabled); ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &TextEdit::is_virtual_keyboard_enabled); - ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); - ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); - ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection); - - ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select); - ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); - ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor); - ClassDB::bind_method(D_METHOD("is_selection_active"), &TextEdit::is_selection_active); - ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line); - ClassDB::bind_method(D_METHOD("get_selection_from_column"), &TextEdit::get_selection_from_column); - ClassDB::bind_method(D_METHOD("get_selection_to_line"), &TextEdit::get_selection_to_line); - ClassDB::bind_method(D_METHOD("get_selection_to_column"), &TextEdit::get_selection_to_column); - ClassDB::bind_method(D_METHOD("get_selection_text"), &TextEdit::get_selection_text); ClassDB::bind_method(D_METHOD("get_word_under_caret"), &TextEdit::get_word_under_caret); ClassDB::bind_method(D_METHOD("search", "key", "flags", "from_line", "from_column"), &TextEdit::_search_bind); @@ -5304,6 +5152,38 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_caret_wrap_index"), &TextEdit::get_caret_wrap_index); + /* Selection. */ + BIND_ENUM_CONSTANT(SELECTION_MODE_NONE); + BIND_ENUM_CONSTANT(SELECTION_MODE_SHIFT); + BIND_ENUM_CONSTANT(SELECTION_MODE_POINTER); + BIND_ENUM_CONSTANT(SELECTION_MODE_WORD); + BIND_ENUM_CONSTANT(SELECTION_MODE_LINE); + + ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); + ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); + + ClassDB::bind_method(D_METHOD("set_selection_mode", "mode", "line", "column"), &TextEdit::set_selection_mode, DEFVAL(-1), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_selection_mode"), &TextEdit::get_selection_mode); + + ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); + ClassDB::bind_method(D_METHOD("select_word_under_caret"), &TextEdit::select_word_under_caret); + ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select); + + ClassDB::bind_method(D_METHOD("has_selection"), &TextEdit::has_selection); + + ClassDB::bind_method(D_METHOD("get_selected_text"), &TextEdit::get_selected_text); + + ClassDB::bind_method(D_METHOD("get_selection_line"), &TextEdit::get_selection_line); + ClassDB::bind_method(D_METHOD("get_selection_column"), &TextEdit::get_selection_column); + + ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line); + ClassDB::bind_method(D_METHOD("get_selection_from_column"), &TextEdit::get_selection_from_column); + ClassDB::bind_method(D_METHOD("get_selection_to_line"), &TextEdit::get_selection_to_line); + ClassDB::bind_method(D_METHOD("get_selection_to_column"), &TextEdit::get_selection_to_column); + + ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); + ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection); + /* line wrapping. */ BIND_ENUM_CONSTANT(LINE_WRAPPING_NONE); BIND_ENUM_CONSTANT(LINE_WRAPPING_BOUNDARY); @@ -5549,7 +5429,7 @@ void TextEdit::_handle_unicode_input(const uint32_t p_unicode) { return; } - bool had_selection = is_selection_active(); + bool had_selection = has_selection(); if (had_selection) { begin_complex_operation(); delete_selection(); @@ -5587,7 +5467,7 @@ void TextEdit::_backspace() { return; } - if (is_selection_active()) { + if (has_selection()) { delete_selection(); return; } @@ -5611,8 +5491,8 @@ void TextEdit::_cut() { return; } - if (is_selection_active()) { - DisplayServer::get_singleton()->clipboard_set(get_selection_text()); + if (has_selection()) { + DisplayServer::get_singleton()->clipboard_set(get_selected_text()); delete_selection(); cut_copy_line = ""; return; @@ -5637,8 +5517,8 @@ void TextEdit::_cut() { } void TextEdit::_copy() { - if (is_selection_active()) { - DisplayServer::get_singleton()->clipboard_set(get_selection_text()); + if (has_selection()) { + DisplayServer::get_singleton()->clipboard_set(get_selected_text()); cut_copy_line = ""; return; } @@ -5659,7 +5539,7 @@ void TextEdit::_paste() { String clipboard = DisplayServer::get_singleton()->clipboard_get(); begin_complex_operation(); - if (is_selection_active()) { + if (has_selection()) { delete_selection(); } else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) { set_caret_column(0); @@ -5697,6 +5577,139 @@ void TextEdit::_toggle_draw_caret() { } } +/* Selection */ +void TextEdit::_click_selection_held() { + // Warning: is_mouse_button_pressed(MOUSE_BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD + // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem. + // I'm unsure if there's an actual fix that doesn't have a ton of side effects. + if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { + switch (selection.selecting_mode) { + case SelectionMode::SELECTION_MODE_POINTER: { + _update_selection_mode_pointer(); + } break; + case SelectionMode::SELECTION_MODE_WORD: { + _update_selection_mode_word(); + } break; + case SelectionMode::SELECTION_MODE_LINE: { + _update_selection_mode_line(); + } break; + default: { + break; + } + } + } else { + click_select_held->stop(); + } +} + +void TextEdit::_update_selection_mode_pointer() { + dragging_selection = true; + Point2 mp = _get_local_mouse_pos(); + + int line, col; + _get_mouse_pos(Point2i(mp.x, mp.y), line, col); + + select(selection.selecting_line, selection.selecting_column, line, col); + + set_caret_line(line, false); + set_caret_column(col); + update(); + + click_select_held->start(); +} + +void TextEdit::_update_selection_mode_word() { + dragging_selection = true; + Point2 mp = _get_local_mouse_pos(); + + int line, col; + _get_mouse_pos(Point2i(mp.x, mp.y), line, col); + + int caret_pos = CLAMP(col, 0, text[line].length()); + int beg = caret_pos; + int end = beg; + Vector words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].x < caret_pos && words[i].y > caret_pos) { + beg = words[i].x; + end = words[i].y; + break; + } + } + + /* Initial selection. */ + if (!selection.active) { + select(line, beg, line, end); + selection.selecting_column = beg; + selection.selected_word_beg = beg; + selection.selected_word_end = end; + selection.selected_word_origin = beg; + set_caret_line(selection.to_line, false); + set_caret_column(selection.to_column); + } else { + if ((col <= selection.selected_word_origin && line == selection.selecting_line) || line < selection.selecting_line) { + selection.selecting_column = selection.selected_word_end; + select(line, beg, selection.selecting_line, selection.selected_word_end); + set_caret_line(selection.from_line, false); + set_caret_column(selection.from_column); + } else { + selection.selecting_column = selection.selected_word_beg; + select(selection.selecting_line, selection.selected_word_beg, line, end); + set_caret_line(selection.to_line, false); + set_caret_column(selection.to_column); + } + } + + update(); + + click_select_held->start(); +} + +void TextEdit::_update_selection_mode_line() { + dragging_selection = true; + Point2 mp = _get_local_mouse_pos(); + + int line, col; + _get_mouse_pos(Point2i(mp.x, mp.y), line, col); + + col = 0; + if (line < selection.selecting_line) { + /* Caret is above us. */ + set_caret_line(line - 1, false); + selection.selecting_column = text[selection.selecting_line].length(); + } else { + /* Caret is below us. */ + set_caret_line(line + 1, false); + selection.selecting_column = 0; + col = text[line].length(); + } + set_caret_column(0); + + select(selection.selecting_line, selection.selecting_column, line, col); + update(); + + click_select_held->start(); +} + +void TextEdit::_pre_shift_selection() { + if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) { + selection.selecting_line = caret.line; + selection.selecting_column = caret.column; + selection.active = true; + } + + selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; +} + +void TextEdit::_post_shift_selection() { + if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) { + select(selection.selecting_line, selection.selecting_column, caret.line, caret.column); + update(); + } + + selection.selecting_text = true; +} + /* Line Wrapping */ void TextEdit::_update_wrap_at_column(bool p_force) { int new_wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding; @@ -5752,23 +5765,25 @@ TextEdit::TextEdit() { v_scroll->connect("scrolling", callable_mp(this, &TextEdit::_v_scroll_input)); + /* Caret. */ caret_blink_timer = memnew(Timer); add_child(caret_blink_timer); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &TextEdit::_toggle_draw_caret)); set_caret_blink_enabled(false); + /* Selection. */ + click_select_held = memnew(Timer); + add_child(click_select_held); + click_select_held->set_wait_time(0.05); + click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held)); + idle_detect = memnew(Timer); 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", callable_mp(this, &TextEdit::_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", callable_mp(this, &TextEdit::_click_selection_held)); - undo_stack_max_size = GLOBAL_GET("gui/common/text_edit_undo_stack_max_size"); set_readonly(false); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 9eb83981ebe..84ee3566737 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -244,6 +244,26 @@ private: bool shiftclick_left = false; } selection; + bool selecting_enabled = true; + + Color font_selected_color = Color(1, 1, 1); + Color selection_color = Color(1, 1, 1); + bool override_selected_font_color = false; + + bool dragging_selection = false; + + Timer *click_select_held; + uint64_t last_dblclk = 0; + Vector2 last_dblclk_pos; + void _click_selection_held(); + + void _update_selection_mode_pointer(); + void _update_selection_mode_word(); + void _update_selection_mode_line(); + + void _pre_shift_selection(); + void _post_shift_selection(); + /* line wrapping. */ LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE; @@ -317,7 +337,6 @@ private: bool first_draw = true; bool draw_tabs = false; bool draw_spaces = false; - bool override_selected_font_color = false; bool text_changed_dirty = false; bool undo_enabled = true; bool hiding_enabled = false; @@ -331,11 +350,9 @@ private: bool highlight_current_line = false; bool insert_mode = false; - bool select_identifiers_enabled = false; bool smooth_scroll_enabled = false; bool scrolling = false; - bool dragging_selection = false; bool dragging_minimap = false; bool can_drag_minimap = false; bool minimap_clicked = false; @@ -346,11 +363,7 @@ private: String lookup_symbol_word; - uint64_t last_dblclk = 0; - Vector2 last_dblclk_pos; - Timer *idle_detect; - Timer *click_select_held; HScrollBar *h_scroll; VScrollBar *v_scroll; bool updating_scrolls = false; @@ -366,8 +379,6 @@ private: int search_result_line = 0; int search_result_col = 0; - bool selecting_enabled = true; - bool context_menu_enabled = true; bool shortcut_keys_enabled = true; bool virtual_keyboard_enabled = true; @@ -394,20 +405,12 @@ private: void _scroll_moved(double); void _update_scrollbars(); void _v_scroll_input(); - void _click_selection_held(); - - void _update_selection_mode_pointer(); - void _update_selection_mode_word(); - void _update_selection_mode_line(); void _update_minimap_click(); void _update_minimap_drag(); void _scroll_up(real_t p_delta); void _scroll_down(real_t p_delta); - void _pre_shift_selection(); - void _post_shift_selection(); - void _scroll_lines_up(); void _scroll_lines_down(); @@ -470,9 +473,7 @@ protected: int outline_size = 0; Color outline_color; Color font_color; - Color font_selected_color; Color font_readonly_color; - Color selection_color; Color code_folding_color; Color current_line_color; Color brace_mismatch_color; @@ -548,6 +549,35 @@ public: int get_caret_wrap_index() const; + /* Selection. */ + void set_selecting_enabled(const bool p_enabled); + bool is_selecting_enabled() const; + + void set_override_selected_font_color(bool p_override_selected_font_color); + bool is_overriding_selected_font_color() const; + + void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1); + SelectionMode get_selection_mode() const; + + void select_all(); + void select_word_under_caret(); + void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column); + + bool has_selection() const; + + String get_selected_text() const; + + int get_selection_line() const; + int get_selection_column() const; + + int get_selection_from_line() const; + int get_selection_from_column() const; + int get_selection_to_line() const; + int get_selection_to_column() const; + + void deselect(); + void delete_selection(); + /* line wrapping. */ void set_line_wrapping_mode(LineWrappingMode p_wrapping_mode); LineWrappingMode get_line_wrapping_mode() const; @@ -710,22 +740,11 @@ public: void adjust_viewport_to_caret(); void center_viewport_to_caret(); - SelectionMode get_selection_mode() const; - void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1); - int get_selection_line() const; - int get_selection_column() const; - void set_readonly(bool p_readonly); bool is_readonly() const; void clear(); - void delete_selection(); - - void select_all(); - void select_word_under_caret(); - void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column); - void deselect(); void swap_lines(int line1, int line2); void set_search_text(const String &p_search_text); @@ -734,12 +753,6 @@ public: void set_highlight_all_occurrences(const bool p_enabled); bool is_highlight_all_occurrences_enabled() const; - bool is_selection_active() const; - int get_selection_from_line() const; - int get_selection_from_column() const; - int get_selection_to_line() const; - int get_selection_to_column() const; - String get_selection_text() const; String get_word_under_caret() const; String get_word_at_pos(const Vector2 &p_pos) const; @@ -756,8 +769,6 @@ public: bool is_drawing_tabs() const; void set_draw_spaces(bool p_draw); bool is_drawing_spaces() const; - void set_override_selected_font_color(bool p_override_selected_font_color); - bool is_overriding_selected_font_color() const; void set_insert_mode(bool p_enabled); bool is_insert_mode() const; @@ -797,9 +808,6 @@ public: void set_context_menu_enabled(bool p_enable); bool is_context_menu_enabled(); - void set_selecting_enabled(bool p_enabled); - bool is_selecting_enabled() const; - void set_shortcut_keys_enabled(bool p_enabled); bool is_shortcut_keys_enabled() const;