summaryrefslogtreecommitdiff
path: root/scene/gui/text_edit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/text_edit.cpp')
-rw-r--r--scene/gui/text_edit.cpp628
1 files changed, 398 insertions, 230 deletions
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 5e165be15e..2cb9d10fca 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -42,10 +42,6 @@
#include "scene/main/window.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
static bool _is_text_char(char32_t c) {
return !is_symbol(c);
}
@@ -107,11 +103,11 @@ void TextEdit::Text::set_direction_and_language(TextServer::Direction p_directio
is_dirty = true;
}
-void TextEdit::Text::set_draw_control_chars(bool p_draw_control_chars) {
- if (draw_control_chars == p_draw_control_chars) {
+void TextEdit::Text::set_draw_control_chars(bool p_enabled) {
+ if (draw_control_chars == p_enabled) {
return;
}
- draw_control_chars = p_draw_control_chars;
+ draw_control_chars = p_enabled;
is_dirty = true;
}
@@ -186,7 +182,7 @@ void TextEdit::Text::_calculate_max_line_width() {
max_width = width;
}
-void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) {
+void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
if (font.is_null() || font_size <= 0) {
@@ -278,14 +274,14 @@ void TextEdit::Text::invalidate_all() {
void TextEdit::Text::clear() {
text.clear();
- insert(0, "", Vector<Vector2i>());
+ insert(0, "", Array());
}
int TextEdit::Text::get_max_width() const {
return max_width;
}
-void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
+void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
text.write[p_line].data = p_text;
@@ -293,7 +289,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i
invalidate_cache(p_line);
}
-void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
+void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_override) {
Line line;
line.gutters.resize(gutter_count);
line.hidden = false;
@@ -304,11 +300,11 @@ void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2
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) {
@@ -334,7 +330,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--;
}
@@ -612,7 +608,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);
@@ -792,8 +788,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++) {
@@ -814,6 +811,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.
@@ -903,6 +902,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];
@@ -1076,11 +1077,14 @@ void TextEdit::_notification(int p_what) {
ofs_y += (row_height - text_height) / 2;
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
- const TextServer::Glyph *glyphs = visual.ptr();
- int gl_size = visual.size();
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
+ 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++) {
@@ -1148,21 +1152,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);
@@ -1174,38 +1193,33 @@ void TextEdit::_notification(int p_what) {
}
}
- // Carets
-#ifdef TOOLS_ENABLED
- int caret_width = Math::round(EDSCALE);
-#else
- int caret_width = 1;
-#endif
+ // Carets.
+ int caret_width = Math::round(1 * get_theme_default_base_scale());
if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) {
caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
if (ime_text.length() == 0) {
- Rect2 l_caret, t_caret;
- TextServer::Direction l_dir, t_dir;
+ CaretInfo ts_caret;
if (str.length() != 0) {
// Get carets.
- TS->shaped_text_get_carets(rid, caret.column, l_caret, l_dir, t_caret, t_dir);
+ ts_caret = TS->shaped_text_get_carets(rid, caret.column);
} else {
// No carets, add one at the start.
int h = font->get_height(font_size);
if (rtl) {
- l_dir = TextServer::DIRECTION_RTL;
- l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h));
+ ts_caret.l_dir = TextServer::DIRECTION_RTL;
+ ts_caret.l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h));
} else {
- l_dir = TextServer::DIRECTION_LTR;
- l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h));
+ ts_caret.l_dir = TextServer::DIRECTION_LTR;
+ ts_caret.l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h));
}
}
- if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
- caret.draw_pos.x = char_margin + ofs_x + l_caret.position.x;
+ if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) {
+ caret.draw_pos.x = char_margin + ofs_x + ts_caret.l_caret.position.x;
} else {
- caret.draw_pos.x = char_margin + ofs_x + t_caret.position.x;
+ caret.draw_pos.x = char_margin + ofs_x + ts_caret.t_caret.position.x;
}
if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) {
@@ -1215,64 +1229,64 @@ void TextEdit::_notification(int p_what) {
//Block or underline caret, draw trailing carets at full height.
int h = font->get_height(font_size);
- if (t_caret != Rect2()) {
+ if (ts_caret.t_caret != Rect2()) {
if (overtype_mode) {
- t_caret.position.y = TS->shaped_text_get_descent(rid);
- t_caret.size.y = caret_width;
+ ts_caret.t_caret.position.y = TS->shaped_text_get_descent(rid);
+ ts_caret.t_caret.size.y = caret_width;
} else {
- t_caret.position.y = -TS->shaped_text_get_ascent(rid);
- t_caret.size.y = h;
+ ts_caret.t_caret.position.y = -TS->shaped_text_get_ascent(rid);
+ ts_caret.t_caret.size.y = h;
}
- t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- draw_rect(t_caret, caret_color, overtype_mode);
+ ts_caret.t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ draw_rect(ts_caret.t_caret, caret_color, overtype_mode);
- if (l_caret != Rect2() && l_dir != t_dir) {
- l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- l_caret.size.x = caret_width;
- draw_rect(l_caret, caret_color * Color(1, 1, 1, 0.5));
+ if (ts_caret.l_caret != Rect2() && ts_caret.l_dir != ts_caret.t_dir) {
+ ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ ts_caret.l_caret.size.x = caret_width;
+ draw_rect(ts_caret.l_caret, caret_color * Color(1, 1, 1, 0.5));
}
} else { // End of the line.
if (gl_size > 0) {
// Adjust for actual line dimensions.
if (overtype_mode) {
- l_caret.position.y = TS->shaped_text_get_descent(rid);
- l_caret.size.y = caret_width;
+ ts_caret.l_caret.position.y = TS->shaped_text_get_descent(rid);
+ ts_caret.l_caret.size.y = caret_width;
} else {
- l_caret.position.y = -TS->shaped_text_get_ascent(rid);
- l_caret.size.y = h;
+ ts_caret.l_caret.position.y = -TS->shaped_text_get_ascent(rid);
+ ts_caret.l_caret.size.y = h;
}
} else if (overtype_mode) {
- l_caret.position.y += l_caret.size.y;
- l_caret.size.y = caret_width;
+ ts_caret.l_caret.position.y += ts_caret.l_caret.size.y;
+ ts_caret.l_caret.size.y = caret_width;
}
- if (l_caret.position.x >= TS->shaped_text_get_size(rid).x) {
- l_caret.size.x = font->get_char_size('m', 0, font_size).x;
+ if (ts_caret.l_caret.position.x >= TS->shaped_text_get_size(rid).x) {
+ ts_caret.l_caret.size.x = font->get_char_size('m', 0, font_size).x;
} else {
- l_caret.size.x = 3 * caret_width;
+ ts_caret.l_caret.size.x = 3 * caret_width;
}
- l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- if (l_dir == TextServer::DIRECTION_RTL) {
- l_caret.position.x -= l_caret.size.x;
+ ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ if (ts_caret.l_dir == TextServer::DIRECTION_RTL) {
+ ts_caret.l_caret.position.x -= ts_caret.l_caret.size.x;
}
- draw_rect(l_caret, caret_color, overtype_mode);
+ draw_rect(ts_caret.l_caret, caret_color, overtype_mode);
}
} else {
// Normal caret.
- if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) {
+ if (ts_caret.l_caret != Rect2() && ts_caret.l_dir == TextServer::DIRECTION_AUTO) {
// Draw extra marker on top of mid caret.
- Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width);
+ Rect2 trect = Rect2(ts_caret.l_caret.position.x - 3 * caret_width, ts_caret.l_caret.position.y, 6 * caret_width, caret_width);
trect.position += Vector2(char_margin + ofs_x, ofs_y);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
}
- l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- l_caret.size.x = caret_width;
+ ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ ts_caret.l_caret.size.x = caret_width;
- draw_rect(l_caret, caret_color);
+ draw_rect(ts_caret.l_caret, caret_color);
- t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- t_caret.size.x = caret_width;
+ ts_caret.t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ ts_caret.t_caret.size.x = caret_width;
- draw_rect(t_caret, caret_color);
+ draw_rect(ts_caret.t_caret, caret_color);
}
}
}
@@ -1318,6 +1332,8 @@ void TextEdit::_notification(int p_what) {
}
}
}
+
+ line_drawing_cache[line] = cache_entry;
}
if (has_focus()) {
@@ -1374,6 +1390,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) {
+ deselect();
+ }
} break;
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
@@ -1413,7 +1433,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()) {
@@ -1424,7 +1444,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()) {
@@ -1435,13 +1455,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);
@@ -1544,7 +1564,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);
@@ -1571,16 +1595,19 @@ 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) {
dragging_minimap = false;
dragging_selection = false;
can_drag_minimap = false;
click_select_held->stop();
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ }
}
// Notify to show soft keyboard.
@@ -1612,7 +1639,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) {
@@ -1680,7 +1707,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;
}
@@ -1801,7 +1828,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();
}
@@ -1897,7 +1924,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;
@@ -1968,10 +1995,10 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
set_caret_line(caret.line - 1);
set_caret_column(text[caret.line].length());
} else {
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
break;
}
}
@@ -2019,10 +2046,10 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
set_caret_line(caret.line + 1);
set_caret_column(0);
} else {
- 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].y > cc) {
- cc = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
break;
}
}
@@ -2214,10 +2241,10 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
int line = caret.line;
int column = caret.column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < column) {
- column = words[i].x;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < column) {
+ column = words[i];
break;
}
}
@@ -2257,10 +2284,10 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
int line = caret.line;
int column = caret.column;
- 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].y > column) {
- column = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > column) {
+ column = words[i];
break;
}
}
@@ -2409,6 +2436,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
}
String TextEdit::get_tooltip(const Point2 &p_pos) const {
+ Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id);
if (!tooltip_obj) {
return Control::get_tooltip(p_pos);
}
@@ -2431,7 +2459,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
}
void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) {
- tooltip_obj = p_obj;
+ ERR_FAIL_NULL(p_obj);
+ tooltip_obj_id = p_obj->get_instance_id();
tooltip_func = p_function;
tooltip_ud = p_udata;
}
@@ -2580,8 +2609,8 @@ bool TextEdit::is_overtype_mode_enabled() const {
return overtype_mode;
}
-void TextEdit::set_context_menu_enabled(bool p_enable) {
- context_menu_enabled = p_enable;
+void TextEdit::set_context_menu_enabled(bool p_enabled) {
+ context_menu_enabled = p_enabled;
}
bool TextEdit::is_context_menu_enabled() const {
@@ -2596,14 +2625,22 @@ bool TextEdit::is_shortcut_keys_enabled() const {
return shortcut_keys_enabled;
}
-void TextEdit::set_virtual_keyboard_enabled(bool p_enable) {
- virtual_keyboard_enabled = p_enable;
+void TextEdit::set_virtual_keyboard_enabled(bool p_enabled) {
+ virtual_keyboard_enabled = p_enabled;
}
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;
@@ -2867,6 +2904,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;
@@ -2923,6 +2970,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();
@@ -3368,7 +3422,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();
@@ -3378,8 +3432,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 {
@@ -3391,37 +3446,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);
@@ -3453,7 +3557,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 {
@@ -3512,8 +3616,8 @@ void TextEdit::set_caret_blink_speed(const float p_speed) {
caret_blink_timer->set_wait_time(p_speed);
}
-void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enable) {
- move_caret_on_right_click = p_enable;
+void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enabled) {
+ move_caret_on_right_click = p_enabled;
}
bool TextEdit::is_move_caret_on_right_click_enabled() const {
@@ -3631,10 +3735,12 @@ int TextEdit::get_caret_wrap_index() const {
}
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++) {
- if (words[i].x <= caret.column && words[i].y > caret.column) {
- return text[caret.line].substr(words[i].x, words[i].y - words[i].x);
+ 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) {
+ return text[caret.line].substr(words[i], words[i + 1] - words[i]);
}
}
return "";
@@ -3653,6 +3759,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;
}
@@ -3666,8 +3783,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;
}
@@ -3718,11 +3837,11 @@ void TextEdit::select_word_under_caret() {
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) || (i == words.size() - 1 && caret.column == words[i].y)) {
- begin = words[i].x;
- end = words[i].y;
+ const 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) || (i == words.size() - 2 && caret.column == words[i + 1])) {
+ begin = words[i];
+ end = words[i + 1];
break;
}
}
@@ -3847,7 +3966,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;
@@ -3921,9 +4040,9 @@ Vector<String> TextEdit::get_line_wrapped_text(int p_line) const {
/* Viewport */
// Scrolling.
-void TextEdit::set_smooth_scroll_enabled(const bool p_enable) {
- v_scroll->set_smooth_scroll_enabled(p_enable);
- smooth_scroll_enabled = p_enable;
+void TextEdit::set_smooth_scroll_enabled(const bool p_enabled) {
+ v_scroll->set_smooth_scroll_enabled(p_enabled);
+ smooth_scroll_enabled = p_enabled;
}
bool TextEdit::is_smooth_scroll_enabled() const {
@@ -3979,15 +4098,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;
}
@@ -4044,14 +4155,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);
@@ -4060,6 +4175,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.
@@ -4182,9 +4301,9 @@ void TextEdit::center_viewport_to_caret() {
}
/* Minimap */
-void TextEdit::set_draw_minimap(bool p_draw) {
- if (draw_minimap != p_draw) {
- draw_minimap = p_draw;
+void TextEdit::set_draw_minimap(bool p_enabled) {
+ if (draw_minimap != p_enabled) {
+ draw_minimap = p_enabled;
_update_wrap_at_column();
}
update();
@@ -4228,7 +4347,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);
@@ -4465,9 +4584,9 @@ bool TextEdit::is_highlight_all_occurrences_enabled() const {
return highlight_all_occurrences;
}
-void TextEdit::set_draw_control_chars(bool p_draw_control_chars) {
- if (draw_control_chars != p_draw_control_chars) {
- draw_control_chars = p_draw_control_chars;
+void TextEdit::set_draw_control_chars(bool p_enabled) {
+ if (draw_control_chars != p_enabled) {
+ draw_control_chars = p_enabled;
if (menu) {
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
}
@@ -4481,8 +4600,8 @@ bool TextEdit::get_draw_control_chars() const {
return draw_control_chars;
}
-void TextEdit::set_draw_tabs(bool p_draw) {
- draw_tabs = p_draw;
+void TextEdit::set_draw_tabs(bool p_enabled) {
+ draw_tabs = p_enabled;
update();
}
@@ -4490,8 +4609,8 @@ bool TextEdit::is_drawing_tabs() const {
return draw_tabs;
}
-void TextEdit::set_draw_spaces(bool p_draw) {
- draw_spaces = p_draw;
+void TextEdit::set_draw_spaces(bool p_enabled) {
+ draw_spaces = p_enabled;
update();
}
@@ -4508,7 +4627,7 @@ void TextEdit::_bind_methods() {
// Text properties
ClassDB::bind_method(D_METHOD("has_ime_text"), &TextEdit::has_ime_text);
- ClassDB::bind_method(D_METHOD("set_editable", "enable"), &TextEdit::set_editable);
+ ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &TextEdit::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &TextEdit::is_editable);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction);
@@ -4533,15 +4652,18 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_overtype_mode_enabled", "enabled"), &TextEdit::set_overtype_mode_enabled);
ClassDB::bind_method(D_METHOD("is_overtype_mode_enabled"), &TextEdit::is_overtype_mode_enabled);
- ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &TextEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled);
- ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &TextEdit::set_shortcut_keys_enabled);
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("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);
@@ -4581,6 +4703,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);
@@ -4646,7 +4769,10 @@ 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);
@@ -4696,6 +4822,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);
@@ -4721,7 +4850,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);
@@ -4767,6 +4896,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
@@ -4774,7 +4904,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("center_viewport_to_caret"), &TextEdit::center_viewport_to_caret);
// Minimap
- ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap);
+ ClassDB::bind_method(D_METHOD("set_draw_minimap", "enabled"), &TextEdit::set_draw_minimap);
ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap);
ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width);
@@ -4830,16 +4960,16 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line);
ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled);
- ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences);
+ ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enabled"), &TextEdit::set_highlight_all_occurrences);
ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled);
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_draw_control_chars", "enabled"), &TextEdit::set_draw_control_chars);
- ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
+ ClassDB::bind_method(D_METHOD("set_draw_tabs", "enabled"), &TextEdit::set_draw_tabs);
ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
- ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces);
+ ClassDB::bind_method(D_METHOD("set_draw_spaces", "enabled"), &TextEdit::set_draw_spaces);
ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
@@ -4855,8 +4985,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");
@@ -4876,7 +5007,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_GROUP("Minimap", "minimap_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "set_draw_minimap", "is_drawing_minimap");
ADD_PROPERTY(PropertyInfo(Variant::INT, "minimap_width"), "set_minimap_width", "get_minimap_width");
ADD_GROUP("Caret", "caret_");
@@ -5082,10 +5213,12 @@ void TextEdit::_cut_internal() {
}
int cl = get_caret_line();
+ int cc = get_caret_column();
+ int indent_level = get_indent_level(cl);
+ double hscroll = get_h_scroll();
String clipboard = text[cl];
DisplayServer::get_singleton()->clipboard_set(clipboard);
- set_caret_line(cl);
set_caret_column(0);
if (cl == 0 && get_line_count() > 1) {
@@ -5096,6 +5229,17 @@ void TextEdit::_cut_internal() {
set_caret_line(get_caret_line() + 1);
}
+ // Correct the visualy perceived caret column taking care of identation level of the lines.
+ int diff_indent = indent_level - get_indent_level(get_caret_line());
+ cc += diff_indent;
+ if (diff_indent != 0) {
+ cc += diff_indent > 0 ? -1 : 1;
+ }
+
+ // Restore horizontal scroll and caret column modified by the backspace() call.
+ set_h_scroll(hscroll);
+ set_caret_column(cc);
+
cut_copy_line = clipboard;
}
@@ -5134,6 +5278,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() {
@@ -5143,32 +5305,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));
@@ -5179,29 +5341,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);
@@ -5214,25 +5376,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();
@@ -5376,23 +5538,21 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const {
}
}
- Rect2 l_caret, t_caret;
- TextServer::Direction l_dir, t_dir;
RID text_rid = text.get_line_data(p_line)->get_line_rid(row);
- TS->shaped_text_get_carets(text_rid, caret.column, l_caret, l_dir, t_caret, t_dir);
- if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
- return l_caret.position.x;
+ CaretInfo ts_caret = TS->shaped_text_get_carets(text_rid, caret.column);
+ if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) {
+ return ts_caret.l_caret.position.x;
} else {
- return t_caret.position.x;
+ return ts_caret.t_caret.position.x;
}
}
/* 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();
@@ -5440,11 +5600,11 @@ void TextEdit::_update_selection_mode_word() {
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) || (i == words.size() - 1 && caret_pos == words[i].y)) {
- beg = words[i].x;
- end = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = 0; i < words.size(); i = i + 2) {
+ if ((words[i] < caret_pos && words[i + 1] > caret_pos) || (i == words.size() - 2 && caret_pos == words[i + 1])) {
+ beg = words[i];
+ end = words[i + 1];
break;
}
}
@@ -5472,6 +5632,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();
@@ -5499,6 +5663,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();
@@ -5684,7 +5852,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;
}
@@ -5711,7 +5879,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;
}
@@ -6032,7 +6200,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
r_end_line = p_line + substrings.size() - 1;
r_end_column = text[r_end_line].length() - postinsert_text.length();
- TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column);
+ TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column);
if (dir != TextServer::DIRECTION_AUTO) {
input_direction = (TextDirection)dir;
}
@@ -6081,7 +6249,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));