diff options
Diffstat (limited to 'scene/gui/text_edit.cpp')
-rw-r--r-- | scene/gui/text_edit.cpp | 504 |
1 files changed, 399 insertions, 105 deletions
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index dc9a3f337e..bc30bf4447 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -39,6 +39,7 @@ #include "core/os/os.h" #include "core/string/string_builder.h" #include "core/string/translation.h" +#include "label.h" #include "scene/main/window.h" @@ -300,11 +301,11 @@ void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_ invalidate_cache(p_at); } -void TextEdit::Text::remove(int p_at) { - int height = text[p_at].height; - int width = text[p_at].width; +void TextEdit::Text::remove_at(int p_index) { + int height = text[p_index].height; + int width = text[p_index].width; - text.remove(p_at); + text.remove_at(p_index); // If this is the tallest line, we need to get the next tallest. if (height == line_height) { @@ -330,7 +331,7 @@ void TextEdit::Text::add_gutter(int p_at) { void TextEdit::Text::remove_gutter(int p_gutter) { for (int i = 0; i < text.size(); i++) { - text.write[i].gutters.remove(p_gutter); + text.write[i].gutters.remove_at(p_gutter); } gutter_count--; } @@ -608,7 +609,7 @@ void TextEdit::_notification(int p_what) { int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); draw_amount += get_line_wrap_count(first_visible_line + 1); - // minimap + // Draw minimap. if (draw_minimap) { int minimap_visible_lines = get_minimap_visible_lines(); int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); @@ -788,8 +789,9 @@ void TextEdit::_notification(int p_what) { bottom_limit_y -= style_normal->get_margin(SIDE_BOTTOM); } - // draw main text + // Draw main text. caret.visible = false; + line_drawing_cache.clear(); int row_height = get_line_height(); int line = first_visible_line; for (int i = 0; i < draw_amount; i++) { @@ -810,6 +812,8 @@ void TextEdit::_notification(int p_what) { continue; } + LineDrawingCache cache_entry; + Dictionary color_map = _get_line_syntax_highlighting(line); // Ensure we at least use the font color. @@ -899,6 +903,8 @@ void TextEdit::_notification(int p_what) { if (line_wrap_index == 0) { // Only do these if we are on the first wrapped part of a line. + cache_entry.y_offset = ofs_y; + int gutter_offset = style_normal->get_margin(SIDE_LEFT); for (int g = 0; g < gutters.size(); g++) { const GutterInfo gutter = gutters[g]; @@ -1050,7 +1056,7 @@ void TextEdit::_notification(int p_what) { while (highlighted_word_col != -1) { Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + lookup_symbol_word.length() + start); for (int j = 0; j < sel.size(); j++) { - Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height); + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y + (line_spacing / 2), sel[j].y - sel[j].x, row_height); if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { continue; } @@ -1060,9 +1066,9 @@ void TextEdit::_notification(int p_what) { } else if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - rect.position.y = TS->shaped_text_get_ascent(rid) + font->get_underline_position(font_size); - rect.size.y = font->get_underline_thickness(font_size); - draw_rect(rect, font_selected_color); + rect.position.y += ceil(TS->shaped_text_get_ascent(rid)) + ceil(font->get_underline_position(font_size)); + rect.size.y = MAX(1, font->get_underline_thickness(font_size)); + draw_rect(rect, color); } highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1); @@ -1076,6 +1082,10 @@ void TextEdit::_notification(int p_what) { int gl_size = TS->shaped_text_get_glyph_count(rid); ofs_y += ldata->get_line_ascent(line_wrap_index); + + int first_visible_char = TS->shaped_text_get_range(rid).y; + int last_visible_char = TS->shaped_text_get_range(rid).x; + int char_ofs = 0; if (outline_size > 0 && outline_color.a > 0) { for (int j = 0; j < gl_size; j++) { @@ -1143,21 +1153,36 @@ void TextEdit::_notification(int p_what) { } } + bool had_glyphs_drawn = false; for (int k = 0; k < glyphs[j].repeat; k++) { if (!clipped && (char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) { if (glyphs[j].font_rid != RID()) { TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color); + had_glyphs_drawn = true; } else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { TS->draw_hex_code_box(ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color); + had_glyphs_drawn = true; } } char_ofs += glyphs[j].advance; } + + if (had_glyphs_drawn) { + if (first_visible_char > glyphs[j].start) { + first_visible_char = glyphs[j].start; + } else if (last_visible_char < glyphs[j].end) { + last_visible_char = glyphs[j].end; + } + } + if ((char_ofs + char_margin) >= xmargin_end) { break; } } + cache_entry.first_visible_chars.push_back(first_visible_char); + cache_entry.last_visible_chars.push_back(last_visible_char); + // is_line_folded if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && _is_line_hidden(line + 1)) { int xofs = char_ofs + char_margin + ofs_x + (folded_eol_icon->get_width() / 2); @@ -1200,7 +1225,7 @@ void TextEdit::_notification(int p_what) { if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) { caret.visible = true; - if (draw_caret) { + if (draw_caret || drag_caret_force_displayed) { if (caret_type == CaretType::CARET_TYPE_BLOCK || overtype_mode) { //Block or underline caret, draw trailing carets at full height. int h = font->get_height(font_size); @@ -1308,6 +1333,8 @@ void TextEdit::_notification(int p_what) { } } } + + line_drawing_cache[line] = cache_entry; } if (has_focus()) { @@ -1364,6 +1391,10 @@ void TextEdit::_notification(int p_what) { if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); } + + if (deselect_on_focus_loss_enabled && !selection.drag_attempt) { + deselect(); + } } break; case MainLoop::NOTIFICATION_OS_IME_UPDATE: { if (has_focus()) { @@ -1381,6 +1412,30 @@ void TextEdit::_notification(int p_what) { update(); } } break; + case Control::NOTIFICATION_DRAG_BEGIN: { + selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; + drag_action = true; + dragging_minimap = false; + dragging_selection = false; + can_drag_minimap = false; + click_select_held->stop(); + } break; + case Control::NOTIFICATION_DRAG_END: { + if (is_drag_successful()) { + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (is_editable() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) { + delete_selection(); + } else if (deselect_on_focus_loss_enabled) { + deselect(); + } + } + } else { + selection.drag_attempt = false; + } + drag_action = false; + drag_caret_force_displayed = false; + } break; } } @@ -1403,7 +1458,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (mb->is_pressed()) { - if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) { + if (mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_pressed()) { if (mb->is_shift_pressed()) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); } else if (mb->is_alt_pressed()) { @@ -1414,7 +1469,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { _scroll_up(3 * mb->get_factor()); } } - if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) { + if (mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) { if (mb->is_shift_pressed()) { h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); } else if (mb->is_alt_pressed()) { @@ -1425,13 +1480,13 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { _scroll_down(3 * mb->get_factor()); } } - if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT) { + if (mb->get_button_index() == MouseButton::WHEEL_LEFT) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); } - if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT) { + if (mb->get_button_index() == MouseButton::WHEEL_RIGHT) { h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); } - if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->get_button_index() == MouseButton::LEFT) { _reset_caret_blink_timer(); Point2i pos = get_line_column_at_pos(mpos); @@ -1465,6 +1520,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { set_caret_line(row, false, false); set_caret_column(col); + selection.drag_attempt = false; if (mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) { if (!selection.active) { @@ -1508,6 +1564,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { update(); } + } else if (is_mouse_over_selection()) { + selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; + selection.drag_attempt = true; } else { selection.active = false; selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER; @@ -1521,6 +1580,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (!mb->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && mb->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance) { // Triple-click select line. selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE; + selection.drag_attempt = false; _update_selection_mode_line(); last_dblclk = 0; } else if (mb->is_double_click() && text[caret.line].length()) { @@ -1534,7 +1594,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { update(); } - if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) { + if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + paste_primary_clipboard(); + } + + if (mb->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { _reset_caret_blink_timer(); Point2i pos = get_line_column_at_pos(mpos); @@ -1561,16 +1625,25 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { _generate_context_menu(); menu->set_position(get_screen_transform().xform(mpos)); - menu->set_size(Vector2(1, 1)); + menu->reset_size(); menu->popup(); grab_focus(); } } else { - if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->get_button_index() == MouseButton::LEFT) { + if (selection.drag_attempt && is_mouse_over_selection()) { + selection.active = false; + } dragging_minimap = false; dragging_selection = false; can_drag_minimap = false; click_select_held->stop(); + if (!drag_action) { + selection.drag_attempt = false; + } + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } } // Notify to show soft keyboard. @@ -1602,7 +1675,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } - if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging. + if ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging. _reset_caret_blink_timer(); if (draw_minimap && !dragging_selection) { @@ -1652,6 +1725,14 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { hovered_gutter = current_hovered_gutter; update(); } + + if (drag_action && can_drop_data(mpos, get_viewport()->gui_get_drag_data())) { + drag_caret_force_displayed = true; + Point2i pos = get_line_column_at_pos(get_local_mouse_pos()); + set_caret_line(pos.y, false); + set_caret_column(pos.x); + dragging_selection = true; + } } if (draw_minimap && !dragging_selection) { @@ -1670,7 +1751,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } // If a modifier has been pressed, and nothing else, return. - if (k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) { + if (k->get_keycode() == Key::CTRL || k->get_keycode() == Key::ALT || k->get_keycode() == Key::SHIFT || k->get_keycode() == Key::META) { return; } @@ -1791,7 +1872,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { _generate_context_menu(); adjust_viewport_to_caret(); menu->set_position(get_screen_transform().xform(get_caret_draw_pos())); - menu->set_size(Vector2(1, 1)); + menu->reset_size(); menu->popup(); menu->grab_focus(); } @@ -1887,7 +1968,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } // Handle Unicode (if no modifiers active). Tab has a value of 0x09. - if (allow_unicode_handling && editable && (k->get_unicode() >= 32 || k->get_keycode() == KEY_TAB)) { + if (allow_unicode_handling && editable && (k->get_unicode() >= 32 || k->get_keycode() == Key::TAB)) { handle_unicode_input(k->get_unicode()); accept_event(); return; @@ -2369,6 +2450,75 @@ bool TextEdit::is_text_field() const { return true; } +Variant TextEdit::get_drag_data(const Point2 &p_point) { + if (selection.active && selection.drag_attempt) { + String t = get_selected_text(); + Label *l = memnew(Label); + l->set_text(t); + set_drag_preview(l); + return t; + } + + return Variant(); +} + +bool TextEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + bool drop_override = Control::can_drop_data(p_point, p_data); // In case user wants to drop custom data. + if (drop_override) { + return drop_override; + } + + return is_editable() && p_data.get_type() == Variant::STRING; +} + +void TextEdit::drop_data(const Point2 &p_point, const Variant &p_data) { + Control::drop_data(p_point, p_data); + + if (p_data.get_type() == Variant::STRING && is_editable()) { + Point2i pos = get_line_column_at_pos(get_local_mouse_pos()); + int caret_row_tmp = pos.y; + int caret_column_tmp = pos.x; + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (!is_mouse_over_selection(!Input::get_singleton()->is_key_pressed(Key::CTRL))) { + begin_complex_operation(); + if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) { + if (caret_row_tmp > selection.to_line) { + caret_row_tmp = caret_row_tmp - (selection.to_line - selection.from_line); + } else if (caret_row_tmp == selection.to_line && caret_column_tmp >= selection.to_column) { + caret_column_tmp = caret_column_tmp - (selection.to_column - selection.from_column); + } + delete_selection(); + } else { + deselect(); + } + + set_caret_line(caret_row_tmp, true, false); + set_caret_column(caret_column_tmp); + insert_text_at_caret(p_data); + end_complex_operation(); + } + } else if (is_mouse_over_selection()) { + caret_row_tmp = selection.from_line; + caret_column_tmp = selection.from_column; + set_caret_line(caret_row_tmp, true, false); + set_caret_column(caret_column_tmp); + insert_text_at_caret(p_data); + grab_focus(); + } else { + deselect(); + set_caret_line(caret_row_tmp, true, false); + set_caret_column(caret_column_tmp); + insert_text_at_caret(p_data); + grab_focus(); + } + + if (caret_row_tmp != caret.line || caret_column_tmp != caret.column) { + select(caret_row_tmp, caret_column_tmp, caret.line, caret.column); + } + } +} + Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { Point2i pos = get_line_column_at_pos(p_pos); int row = pos.y; @@ -2596,6 +2746,14 @@ bool TextEdit::is_virtual_keyboard_enabled() const { return virtual_keyboard_enabled; } +void TextEdit::set_middle_mouse_paste_enabled(bool p_enabled) { + middle_mouse_paste_enabled = p_enabled; +} + +bool TextEdit::is_middle_mouse_paste_enabled() const { + return middle_mouse_paste_enabled; +} + // Text manipulation void TextEdit::clear() { setting_text = true; @@ -2859,6 +3017,16 @@ Point2i TextEdit::get_next_visible_line_index_offset_from(int p_line_from, int p } } wrap_index = get_line_wrap_count(MIN(i, text.size() - 1)) - MAX(0, num_visible - p_visible_amount); + + // If we are a hidden line, then we are the last line as we cannot reach "p_visible_amount". + // This means we need to backtrack to get last visible line. + // Currently, line 0 cannot be hidden so this should always be valid. + int line = (p_line_from + num_total) - 1; + if (_is_line_hidden(line)) { + Point2i backtrack = get_next_visible_line_index_offset_from(line, 0, -1); + num_total = num_total - (backtrack.x - 1); + wrap_index = backtrack.y; + } } else { p_visible_amount = ABS(p_visible_amount); int i; @@ -2915,6 +3083,13 @@ void TextEdit::paste() { _paste_internal(); } +void TextEdit::paste_primary_clipboard() { + if (GDVIRTUAL_CALL(_paste_primary_clipboard)) { + return; + } + _paste_primary_clipboard_internal(); +} + // Context menu. PopupMenu *TextEdit::get_menu() const { const_cast<TextEdit *>(this)->_generate_context_menu(); @@ -3360,7 +3535,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { return String(); } -Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const { +Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_of_bounds) const { float rows = p_pos.y; rows -= style_normal->get_margin(SIDE_TOP); rows /= get_line_height(); @@ -3370,8 +3545,9 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const { int wrap_index = 0; if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || _is_hiding_enabled()) { - Point2i f_ofs = get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, rows + (1 * SGN(rows))); + Point2i f_ofs = get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, rows + (1 * SIGN(rows))); wrap_index = f_ofs.y; + if (rows < 0) { row = first_vis_line - (f_ofs.x - 1); } else { @@ -3383,37 +3559,86 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const { row = 0; } - int col = 0; - if (row >= text.size()) { row = text.size() - 1; - col = text[row].size(); - } else { - int colx = p_pos.x - (style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); - colx += caret.x_ofs; - col = _get_char_pos_for_line(colx, row, wrap_index); - if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) { - // Move back one if we are at the end of the row. - Vector<String> rows2 = get_line_wrapped_text(row); - int row_end_col = 0; - for (int i = 0; i < wrap_index + 1; i++) { - row_end_col += rows2[i].length(); - } - if (col >= row_end_col) { - col -= 1; - } + } + + int visible_lines = get_visible_line_count_in_range(first_vis_line, row); + if (rows > visible_lines) { + if (!p_allow_out_of_bounds) { + return Point2i(-1, -1); } + return Point2i(text[row].size(), row); + } - RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); - if (is_layout_rtl()) { - colx = TS->shaped_text_get_size(text_rid).x - colx; + int col = 0; + int colx = p_pos.x - (style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); + colx += caret.x_ofs; + col = _get_char_pos_for_line(colx, row, wrap_index); + if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) { + // Move back one if we are at the end of the row. + Vector<String> rows2 = get_line_wrapped_text(row); + int row_end_col = 0; + for (int i = 0; i < wrap_index + 1; i++) { + row_end_col += rows2[i].length(); + } + if (col >= row_end_col) { + col -= 1; } - col = TS->shaped_text_hit_test_position(text_rid, colx); } + RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); + if (is_layout_rtl()) { + colx = TS->shaped_text_get_size(text_rid).x - colx; + } + col = TS->shaped_text_hit_test_position(text_rid, colx); + return Point2i(col, row); } +Point2i TextEdit::get_pos_at_line_column(int p_line, int p_column) const { + Rect2i rect = get_rect_at_line_column(p_line, p_column); + return rect.position + Vector2i(0, get_line_height()); +} + +Rect2i TextEdit::get_rect_at_line_column(int p_line, int p_column) const { + ERR_FAIL_INDEX_V(p_line, text.size(), Rect2i(-1, -1, 0, 0)); + ERR_FAIL_COND_V(p_column < 0, Rect2i(-1, -1, 0, 0)); + ERR_FAIL_COND_V(p_column > text[p_line].length(), Rect2i(-1, -1, 0, 0)); + + if (line_drawing_cache.size() == 0 || !line_drawing_cache.has(p_line)) { + // Line is not in the cache, which means it's outside of the viewing area. + return Rect2i(-1, -1, 0, 0); + } + LineDrawingCache cache_entry = line_drawing_cache[p_line]; + + int wrap_index = get_line_wrap_index_at_column(p_line, p_column); + if (wrap_index >= cache_entry.first_visible_chars.size()) { + // Line seems to be wrapped beyond the viewable area. + return Rect2i(-1, -1, 0, 0); + } + + int first_visible_char = cache_entry.first_visible_chars[wrap_index]; + int last_visible_char = cache_entry.last_visible_chars[wrap_index]; + if (p_column < first_visible_char || p_column > last_visible_char) { + // Character is outside of the viewing area, no point calculating its position. + return Rect2i(-1, -1, 0, 0); + } + + Point2i pos, size; + pos.y = cache_entry.y_offset + get_line_height() * wrap_index; + pos.x = get_total_gutter_width() + style_normal->get_margin(SIDE_LEFT) - get_h_scroll(); + + RID text_rid = text.get_line_data(p_line)->get_line_rid(wrap_index); + Vector2 col_bounds = TS->shaped_text_get_grapheme_bounds(text_rid, p_column); + pos.x += col_bounds.x; + size.x = col_bounds.y - col_bounds.x; + + size.y = get_line_height(); + + return Rect2i(pos, size); +} + int TextEdit::get_minimap_line_at_pos(const Point2i &p_pos) const { float rows = p_pos.y; rows -= style_normal->get_margin(SIDE_TOP); @@ -3445,7 +3670,7 @@ int TextEdit::get_minimap_line_at_pos(const Point2i &p_pos) const { int row = minimap_line + Math::floor(rows); if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || _is_hiding_enabled()) { - int f_ofs = get_next_visible_line_index_offset_from(minimap_line, caret.wrap_ofs, rows + (1 * SGN(rows))).x - 1; + int f_ofs = get_next_visible_line_index_offset_from(minimap_line, caret.wrap_ofs, rows + (1 * SIGN(rows))).x - 1; if (rows < 0) { row = minimap_line - f_ofs; } else { @@ -3468,6 +3693,21 @@ bool TextEdit::is_dragging_cursor() const { return dragging_selection || dragging_minimap; } +bool TextEdit::is_mouse_over_selection(bool p_edges) const { + if (!has_selection()) { + return false; + } + Point2i pos = get_line_column_at_pos(get_local_mouse_pos()); + int row = pos.y; + int col = pos.x; + if (p_edges) { + if ((row == selection.from_line && col == selection.from_column) || (row == selection.to_line && col == selection.to_column)) { + return true; + } + } + return (row >= selection.from_line && row <= selection.to_line && (row > selection.from_line || col > selection.from_column) && (row < selection.to_line || col < selection.to_column)); +} + /* Caret */ void TextEdit::set_caret_type(CaretType p_type) { caret_type = p_type; @@ -3623,6 +3863,8 @@ int TextEdit::get_caret_wrap_index() const { } String TextEdit::get_word_under_caret() const { + ERR_FAIL_INDEX_V(caret.line, text.size(), ""); + ERR_FAIL_INDEX_V(caret.column, text[caret.line].length() + 1, ""); PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); for (int i = 0; i < words.size(); i = i + 2) { if (words[i] <= caret.column && words[i + 1] > caret.column) { @@ -3645,6 +3887,17 @@ bool TextEdit::is_selecting_enabled() const { return selecting_enabled; } +void TextEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + deselect_on_focus_loss_enabled = p_enabled; + if (p_enabled && selection.active && !has_focus()) { + deselect(); + } +} + +bool TextEdit::is_deselect_on_focus_loss_enabled() const { + return deselect_on_focus_loss_enabled; +} + void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { override_selected_font_color = p_override_selected_font_color; } @@ -3658,8 +3911,10 @@ void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column if (p_line >= 0) { ERR_FAIL_INDEX(p_line, text.size()); selection.selecting_line = p_line; + selection.selecting_column = CLAMP(selection.selecting_column, 0, text[selection.selecting_line].length()); } if (p_column >= 0) { + ERR_FAIL_INDEX(selection.selecting_line, text.size()); ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length()); selection.selecting_column = p_column; } @@ -3839,7 +4094,7 @@ void TextEdit::delete_selection() { update(); } -/* line wrapping. */ +/* Line wrapping. */ void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) { if (line_wrapping_mode != p_wrapping_mode) { line_wrapping_mode = p_wrapping_mode; @@ -3971,15 +4226,7 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const { return p_line; } - // Count the number of visible lines up to this line. - double new_line_scroll_pos = 0.0; - int to = CLAMP(p_line, 0, text.size() - 1); - for (int i = 0; i < to; i++) { - if (!text.is_hidden(i)) { - new_line_scroll_pos++; - new_line_scroll_pos += get_line_wrap_count(i); - } - } + double new_line_scroll_pos = get_visible_line_count_in_range(0, CLAMP(p_line, 0, text.size() - 1)); new_line_scroll_pos += p_wrap_index; return new_line_scroll_pos; } @@ -4036,14 +4283,18 @@ int TextEdit::get_visible_line_count() const { return _get_control_height() / get_line_height(); } -int TextEdit::get_total_visible_line_count() const { - /* Returns the total number of (lines + wraped - hidden). */ +int TextEdit::get_visible_line_count_in_range(int p_from_line, int p_to_line) const { + ERR_FAIL_INDEX_V(p_from_line, text.size(), 0); + ERR_FAIL_INDEX_V(p_to_line, text.size(), 0); + ERR_FAIL_COND_V(p_from_line > p_to_line, 0); + + /* Returns the total number of (lines + wrapped - hidden). */ if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { - return text.size(); + return p_to_line - p_from_line; } int total_rows = 0; - for (int i = 0; i < text.size(); i++) { + for (int i = p_from_line; i <= p_to_line; i++) { if (!text.is_hidden(i)) { total_rows++; total_rows += get_line_wrap_count(i); @@ -4052,6 +4303,10 @@ int TextEdit::get_total_visible_line_count() const { return total_rows; } +int TextEdit::get_total_visible_line_count() const { + return get_visible_line_count_in_range(0, text.size() - 1); +} + // Auto adjust void TextEdit::adjust_viewport_to_caret() { // Make sure Caret is visible on the screen. @@ -4220,7 +4475,7 @@ void TextEdit::add_gutter(int p_at) { void TextEdit::remove_gutter(int p_gutter) { ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.remove(p_gutter); + gutters.remove_at(p_gutter); for (int i = 0; i < text.size() + 1; i++) { text.remove_gutter(p_gutter); @@ -4534,6 +4789,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enabled"), &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_middle_mouse_paste_enabled", "enabled"), &TextEdit::set_middle_mouse_paste_enabled); + ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &TextEdit::is_middle_mouse_paste_enabled); + // Text manipulation ClassDB::bind_method(D_METHOD("clear"), &TextEdit::clear); @@ -4573,6 +4831,7 @@ void TextEdit::_bind_methods() { GDVIRTUAL_BIND(_cut) GDVIRTUAL_BIND(_copy) GDVIRTUAL_BIND(_paste) + GDVIRTUAL_BIND(_paste_primary_clipboard) // Context Menu BIND_ENUM_CONSTANT(MENU_CUT); @@ -4638,10 +4897,14 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_word_at_pos", "position"), &TextEdit::get_word_at_pos); - ClassDB::bind_method(D_METHOD("get_line_column_at_pos", "position"), &TextEdit::get_line_column_at_pos); + ClassDB::bind_method(D_METHOD("get_line_column_at_pos", "position", "allow_out_of_bounds"), &TextEdit::get_line_column_at_pos, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_pos_at_line_column", "line", "column"), &TextEdit::get_pos_at_line_column); + ClassDB::bind_method(D_METHOD("get_rect_at_line_column", "line", "column"), &TextEdit::get_rect_at_line_column); + ClassDB::bind_method(D_METHOD("get_minimap_line_at_pos", "position"), &TextEdit::get_minimap_line_at_pos); ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor); + ClassDB::bind_method(D_METHOD("is_mouse_over_selection", "edges"), &TextEdit::is_mouse_over_selection); /* Caret. */ BIND_ENUM_CONSTANT(CARET_TYPE_LINE); @@ -4688,6 +4951,9 @@ void TextEdit::_bind_methods() { 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_deselect_on_focus_loss_enabled", "enable"), &TextEdit::set_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &TextEdit::is_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color); ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color); @@ -4713,7 +4979,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection); - /* line wrapping. */ + /* Line wrapping. */ BIND_ENUM_CONSTANT(LINE_WRAPPING_NONE); BIND_ENUM_CONSTANT(LINE_WRAPPING_BOUNDARY); @@ -4759,6 +5025,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_last_full_visible_line_wrap_index"), &TextEdit::get_last_full_visible_line_wrap_index); ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_visible_line_count); + ClassDB::bind_method(D_METHOD("get_visible_line_count_in_range", "from_line", "to_line"), &TextEdit::get_visible_line_count_in_range); ClassDB::bind_method(D_METHOD("get_total_visible_line_count"), &TextEdit::get_total_visible_line_count); // Auto adjust @@ -4847,8 +5114,9 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); - + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); @@ -5139,6 +5407,24 @@ void TextEdit::_paste_internal() { end_complex_operation(); } +void TextEdit::_paste_primary_clipboard_internal() { + if (!is_editable() || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + return; + } + + String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary(); + + Point2i pos = get_line_column_at_pos(get_local_mouse_pos()); + deselect(); + set_caret_line(pos.y, true, false); + set_caret_column(pos.x); + if (!paste_buffer.is_empty()) { + insert_text_at_caret(paste_buffer); + } + + grab_focus(); +} + /* Text. */ // Context menu. void TextEdit::_generate_context_menu() { @@ -5148,32 +5434,32 @@ void TextEdit::_generate_context_menu() { menu_dir = memnew(PopupMenu); menu_dir->set_name("DirMenu"); - menu_dir->add_radio_check_item(RTR("Same as layout direction"), MENU_DIR_INHERITED); - menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO); - menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR); - menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL); + menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); menu_ctl = memnew(PopupMenu); menu_ctl->set_name("CTLMenu"); - menu_ctl->add_item(RTR("Left-to-right mark (LRM)"), MENU_INSERT_LRM); - menu_ctl->add_item(RTR("Right-to-left mark (RLM)"), MENU_INSERT_RLM); - menu_ctl->add_item(RTR("Start of left-to-right embedding (LRE)"), MENU_INSERT_LRE); - menu_ctl->add_item(RTR("Start of right-to-left embedding (RLE)"), MENU_INSERT_RLE); - menu_ctl->add_item(RTR("Start of left-to-right override (LRO)"), MENU_INSERT_LRO); - menu_ctl->add_item(RTR("Start of right-to-left override (RLO)"), MENU_INSERT_RLO); - menu_ctl->add_item(RTR("Pop direction formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Arabic letter mark (ALM)"), MENU_INSERT_ALM); - menu_ctl->add_item(RTR("Left-to-right isolate (LRI)"), MENU_INSERT_LRI); - menu_ctl->add_item(RTR("Right-to-left isolate (RLI)"), MENU_INSERT_RLI); - menu_ctl->add_item(RTR("First strong isolate (FSI)"), MENU_INSERT_FSI); - menu_ctl->add_item(RTR("Pop direction isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Zero width joiner (ZWJ)"), MENU_INSERT_ZWJ); - menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ); - menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ); - menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY); + menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); @@ -5184,29 +5470,29 @@ void TextEdit::_generate_context_menu() { // Reorganize context menu. menu->clear(); if (editable) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0); + menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE); } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0); + menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0); + menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE); } menu->add_separator(); if (is_selecting_enabled()) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); } if (editable) { menu->add_item(RTR("Clear"), MENU_CLEAR); menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0); + menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE); + menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE); } menu->add_separator(); - menu->add_submenu_item(RTR("Text writing direction"), "DirMenu"); + menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu"); menu->add_separator(); - menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC); + menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); if (editable) { - menu->add_submenu_item(RTR("Insert control character"), "CTLMenu"); + menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu"); } menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); @@ -5219,25 +5505,25 @@ void TextEdit::_generate_context_menu() { } } -int TextEdit::_get_menu_action_accelerator(const String &p_action) { +Key TextEdit::_get_menu_action_accelerator(const String &p_action) { const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); if (!events) { - return 0; + return Key::NONE; } // Use first event in the list for the accelerator. const List<Ref<InputEvent>>::Element *first_event = events->front(); if (!first_event) { - return 0; + return Key::NONE; } const Ref<InputEventKey> event = first_event->get(); if (event.is_null()) { - return 0; + return Key::NONE; } // Use physical keycode if non-zero - if (event->get_physical_keycode() != 0) { + if (event->get_physical_keycode() != Key::NONE) { return event->get_physical_keycode_with_modifiers(); } else { return event->get_keycode_with_modifiers(); @@ -5392,10 +5678,10 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const { /* 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 + // Warning: is_mouse_button_pressed(MouseButton::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) { + if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { switch (selection.selecting_mode) { case SelectionMode::SELECTION_MODE_POINTER: { _update_selection_mode_pointer(); @@ -5475,6 +5761,10 @@ void TextEdit::_update_selection_mode_word() { } } + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } + update(); click_select_held->start(); @@ -5502,6 +5792,10 @@ void TextEdit::_update_selection_mode_line() { set_caret_column(0); select(selection.selecting_line, selection.selecting_column, line, col); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); + } + update(); click_select_held->start(); @@ -5687,7 +5981,7 @@ double TextEdit::_get_v_scroll_offset() const { } void TextEdit::_scroll_up(real_t p_delta) { - if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta)) { + if (scrolling && smooth_scroll_enabled && SIGN(target_v_scroll - v_scroll->get_value()) != SIGN(-p_delta)) { scrolling = false; minimap_clicked = false; } @@ -5714,7 +6008,7 @@ void TextEdit::_scroll_up(real_t p_delta) { } void TextEdit::_scroll_down(real_t p_delta) { - if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta)) { + if (scrolling && smooth_scroll_enabled && SIGN(target_v_scroll - v_scroll->get_value()) != SIGN(p_delta)) { scrolling = false; minimap_clicked = false; } @@ -6084,7 +6378,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length()); for (int i = p_from_line; i < p_to_line; i++) { - text.remove(p_from_line + 1); + text.remove_at(p_from_line + 1); } text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text)); |