diff options
author | Paulb23 <p_batty@hotmail.co.uk> | 2021-07-09 12:42:55 +0100 |
---|---|---|
committer | Paulb23 <p_batty@hotmail.co.uk> | 2021-08-12 09:29:56 +0100 |
commit | 9ec3e7f3d7ae7f9eaf5a1de29346c5e10b3af6f9 (patch) | |
tree | 2953e06268871695e0c49b12f16372ce672bf027 /scene/gui | |
parent | 7e70f9e0b9696fab55e2d0570c6bdc8b07c14462 (diff) |
Cleanup TextEdit selection methods
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/code_edit.cpp | 20 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 795 | ||||
-rw-r--r-- | scene/gui/text_edit.h | 90 |
3 files changed, 464 insertions, 441 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 6528e90f30..134151dbe8 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 e77c8188cc..5cd95fbbf7 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<Vector2i> 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<InputEvent> &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<InputEvent> &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<Vector2i> 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<Vector2i> 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<Vector2i> 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<Vector2i> 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 9eb83981eb..84ee356673 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; |