diff options
Diffstat (limited to 'scene/gui/line_edit.cpp')
-rw-r--r-- | scene/gui/line_edit.cpp | 1177 |
1 files changed, 710 insertions, 467 deletions
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index ce371e3b56..540250c8e9 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,6 @@ #include "servers/display_server.h" #include "servers/text_server.h" #ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" #include "editor/editor_settings.h" #endif #include "scene/main/window.h" @@ -51,13 +50,13 @@ void LineEdit::_swap_current_input_direction() { } else { input_direction = TEXT_DIRECTION_LTR; } - set_cursor_position(get_cursor_position()); + set_caret_column(get_caret_column()); update(); } -void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { +void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (selection.enabled && !p_select) { - set_cursor_position(selection.begin); + set_caret_column(selection.begin); deselect(); return; } @@ -65,31 +64,31 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { shift_selection_check_pre(p_select); if (p_move_by_word) { - int cc = cursor_pos; + int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_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_rid); + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < cc) { + cc = words[i]; break; } } - set_cursor_position(cc); + set_caret_column(cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() - 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() - 1); } else { - set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position())); + set_caret_column(TS->shaped_text_prev_grapheme_pos(text_rid, get_caret_column())); } } shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { +void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (selection.enabled && !p_select) { - set_cursor_position(selection.end); + set_caret_column(selection.end); deselect(); return; } @@ -97,37 +96,37 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { shift_selection_check_pre(p_select); if (p_move_by_word) { - int cc = cursor_pos; + int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_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_rid); + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > cc) { + cc = words[i]; break; } } - set_cursor_position(cc); + set_caret_column(cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() + 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() + 1); } else { - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position())); + set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, get_caret_column())); } } shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_start(bool p_select) { +void LineEdit::_move_caret_start(bool p_select) { shift_selection_check_pre(p_select); - set_cursor_position(0); + set_caret_column(0); shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_end(bool p_select) { +void LineEdit::_move_caret_end(bool p_select) { shift_selection_check_pre(p_select); - set_cursor_position(text.length()); + set_caret_column(text.length()); shift_selection_check_post(p_select); } @@ -138,7 +137,7 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { if (p_all_to_left) { deselect(); - text = text.substr(0, cursor_pos); + text = text.substr(0, caret_column); _text_changed(); return; } @@ -149,18 +148,19 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { } if (p_word) { - int cc = cursor_pos; + int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_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_rid); + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < cc) { + cc = words[i]; + break; } } - delete_text(cc, cursor_pos); + delete_text(cc, caret_column); - set_cursor_position(cc); + set_caret_column(cc); } else { delete_char(); } @@ -173,9 +173,9 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { if (p_all_to_right) { deselect(); - text = text.substr(cursor_pos, text.length() - cursor_pos); + text = text.substr(caret_column, text.length() - caret_column); _shape(); - set_cursor_position(0); + set_caret_column(0); _text_changed(); return; } @@ -187,34 +187,58 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { int text_len = text.length(); - if (cursor_pos == text_len) { + if (caret_column == text_len) { return; // Nothing to do. } if (p_word) { - int cc = cursor_pos; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; + int cc = caret_column; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > cc) { + cc = words[i]; break; } } - delete_text(cursor_pos, cc); + delete_text(caret_column, cc); + set_caret_column(caret_column); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(cursor_pos + 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(caret_column + 1); delete_char(); } else { - int cc = cursor_pos; - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos)); - delete_text(cc, cursor_pos); + int cc = caret_column; + set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, caret_column)); + delete_text(cc, caret_column); } } } -void LineEdit::_gui_input(Ref<InputEvent> p_event) { +void LineEdit::unhandled_key_input(const Ref<InputEvent> &p_event) { + Ref<InputEventKey> k = p_event; + + if (k.is_valid()) { + if (!k->is_pressed()) { + return; + } + // Handle Unicode (with modifiers active, process after shortcuts). + if (has_focus() && editable && (k->get_unicode() >= 32)) { + selection_delete(); + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; + int prev_len = text.length(); + insert_text_at_caret(ucodestr); + if (text.length() != prev_len) { + _text_changed(); + } + accept_event(); + } + } +} + +void LineEdit::gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { @@ -222,23 +246,42 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { // Ignore mouse clicks in IME input mode. return; } - if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { - menu->set_position(get_screen_transform().xform(get_local_mouse_position())); - menu->set_size(Vector2(1, 1)); - _generate_context_menu(); + if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { + _ensure_menu(); + menu->set_position(get_screen_position() + get_local_mouse_position()); + menu->reset_size(); menu->popup(); grab_focus(); accept_event(); return; } - if (b->get_button_index() != BUTTON_LEFT) { + if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes(); + + deselect(); + set_caret_at_pixel_pos(b->get_position().x); + if (!paste_buffer.is_empty()) { + insert_text_at_caret(paste_buffer); + + if (!text_changed_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed"); + } + text_changed_dirty = true; + } + } + grab_focus(); + return; + } + + if (b->get_button_index() != MouseButton::LEFT) { return; } _reset_caret_blink_timer(); if (b->is_pressed()) { - accept_event(); //don't pass event further when clicked on text field + accept_event(); // don't pass event further when clicked on text field if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; @@ -246,48 +289,61 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { return; } - shift_selection_check_pre(b->get_shift()); + if (b->is_shift_pressed()) { + shift_selection_check_pre(true); + } - set_cursor_at_pixel_pos(b->get_position().x); + set_caret_at_pixel_pos(b->get_position().x); - if (b->get_shift()) { - selection_fill_at_cursor(); + if (b->is_shift_pressed()) { + selection_fill_at_caret(); selection.creating = true; } else { if (selecting_enabled) { - if (!b->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) { + const int triple_click_timeout = 600; + const int triple_click_tolerance = 5; + const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance; + + if (is_triple_click && text.length()) { // Triple-click select all. selection.enabled = true; selection.begin = 0; selection.end = text.length(); - selection.doubleclick = true; - selection.last_dblclk = 0; - cursor_pos = selection.begin; - } else if (b->is_doubleclick()) { + selection.double_click = true; + last_dblclk = 0; + set_caret_column(selection.begin); + if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(text); + } + } else if (b->is_double_click()) { // Double-click select word. - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if (words[i].x < cursor_pos && words[i].y > cursor_pos) { + last_dblclk = OS::get_singleton()->get_ticks_msec(); + last_dblclk_pos = b->get_position(); + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_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])) { selection.enabled = true; - selection.begin = words[i].x; - selection.end = words[i].y; - selection.doubleclick = true; - selection.last_dblclk = OS::get_singleton()->get_ticks_msec(); - cursor_pos = selection.end; + selection.begin = words[i]; + selection.end = words[i + 1]; + selection.double_click = true; + set_caret_column(selection.end); break; } } + if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin)); + } } } selection.drag_attempt = false; - if ((cursor_pos < selection.begin) || (cursor_pos > selection.end) || !selection.enabled) { + if ((caret_column < selection.begin) || (caret_column > selection.end) || !selection.enabled) { deselect(); - selection.cursor_start = cursor_pos; + selection.start_column = caret_column; selection.creating = true; - } else if (selection.enabled) { + } else if (selection.enabled && !selection.double_click) { selection.drag_attempt = true; } } @@ -295,6 +351,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { update(); } else { + if (selection.enabled && !pass && b->get_button_index() == MouseButton::LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin)); + } if (!text.is_empty() && is_editable() && clear_button_enabled) { bool press_attempt = clear_button_status.press_attempt; clear_button_status.press_attempt = false; @@ -304,11 +363,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } - if ((!selection.creating) && (!selection.doubleclick)) { + if ((!selection.creating) && (!selection.double_click)) { deselect(); } selection.creating = false; - selection.doubleclick = false; + selection.double_click = false; + if (!drag_action) { + selection.drag_attempt = false; + } show_virtual_keyboard(); } @@ -327,35 +389,76 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } - if (m->get_button_mask() & BUTTON_LEFT) { + if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { if (selection.creating) { - set_cursor_at_pixel_pos(m->get_position().x); - selection_fill_at_cursor(); + set_caret_at_pixel_pos(m->get_position().x); + selection_fill_at_caret(); } } + + if (drag_action && can_drop_data(m->get_position(), get_viewport()->gui_get_drag_data())) { + drag_caret_force_displayed = true; + set_caret_at_pixel_pos(m->get_position().x); + } } Ref<InputEventKey> k = p_event; if (k.is_valid()) { if (!k->is_pressed()) { + if (alt_start && k->get_keycode() == Key::ALT) { + alt_start = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; + insert_text_at_caret(ucodestr); + } + accept_event(); + return; + } return; } + // Alt+ Unicode input: + if (k->is_alt_pressed()) { + if (!alt_start) { + if (k->get_keycode() == Key::KP_ADD) { + alt_start = true; + alt_code = 0; + accept_event(); + return; + } + } else { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); + } + if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); + } + if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; + } + accept_event(); + return; + } + } + if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { - Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); - menu->set_position(get_global_transform().xform(pos)); - menu->set_size(Vector2(1, 1)); - _generate_context_menu(); + _ensure_menu(); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")))) / 2); + menu->set_position(get_screen_position() + pos); + menu->reset_size(); menu->popup(); menu->grab_focus(); } } - // Default is ENTER, KP_ENTER. Cannot use ui_accept as default includes SPACE - if (k->is_action("ui_text_newline", true)) { - emit_signal("text_entered", text); + // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE + if (k->is_action("ui_text_submit", false)) { + emit_signal(SNAME("text_submitted"), text); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); } @@ -438,39 +541,39 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { // Cursor Movement k = k->duplicate(); - bool shift_pressed = k->get_shift(); + bool shift_pressed = k->is_shift_pressed(); // Remove shift or else actions will not match. Use above variable for selection. - k->set_shift(false); + k->set_shift_pressed(false); if (k->is_action("ui_text_caret_word_left", true)) { - _move_cursor_left(shift_pressed, true); + _move_caret_left(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_left", true)) { - _move_cursor_left(shift_pressed); + _move_caret_left(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_word_right", true)) { - _move_cursor_right(shift_pressed, true); + _move_caret_right(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_right", true)) { - _move_cursor_right(shift_pressed, false); + _move_caret_right(shift_pressed, false); accept_event(); return; } // Up = Home, Down = End if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { - _move_cursor_start(shift_pressed); + _move_caret_start(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { - _move_cursor_end(shift_pressed); + _move_caret_end(shift_pressed); accept_event(); return; } @@ -486,14 +589,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { // Allow unicode handling if: // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey()); + bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); if (allow_unicode_handling && editable && k->get_unicode() >= 32) { // Handle Unicode (if no modifiers active) selection_delete(); char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; int prev_len = text.length(); - append_at_cursor(ucodestr); + insert_text_at_caret(ucodestr); if (text.length() != prev_len) { _text_changed(); } @@ -502,17 +605,17 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } -void LineEdit::set_align(Align p_align) { - ERR_FAIL_INDEX((int)p_align, 4); - if (align != p_align) { - align = p_align; +void LineEdit::set_horizontal_alignment(HorizontalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (alignment != p_alignment) { + alignment = p_alignment; _shape(); } update(); } -LineEdit::Align LineEdit::get_align() const { - return align; +HorizontalAlignment LineEdit::get_horizontal_alignment() const { + return alignment; } Variant LineEdit::get_drag_data(const Point2 &p_point) { @@ -533,27 +636,55 @@ bool LineEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const return drop_override; } - return p_data.get_type() == Variant::STRING; + return is_editable() && p_data.get_type() == Variant::STRING; } void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { Control::drop_data(p_point, p_data); - if (p_data.get_type() == Variant::STRING) { - set_cursor_at_pixel_pos(p_point.x); - int selected = selection.end - selection.begin; - - text.erase(selection.begin, selected); - _shape(); + if (p_data.get_type() == Variant::STRING && is_editable()) { + set_caret_at_pixel_pos(p_point.x); + int caret_column_tmp = caret_column; + bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end; + if (Input::get_singleton()->is_key_pressed(Key::CTRL)) { + is_inside_sel = selection.enabled && caret_column > selection.begin && caret_column < selection.end; + } + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (!is_inside_sel) { + if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) { + if (caret_column_tmp > selection.end) { + caret_column_tmp = caret_column_tmp - (selection.end - selection.begin); + } + selection_delete(); + } - append_at_cursor(p_data); - selection.begin = cursor_pos - selected; - selection.end = cursor_pos; + set_caret_column(caret_column_tmp); + insert_text_at_caret(p_data); + } + } else if (selection.enabled && caret_column >= selection.begin && caret_column <= selection.end) { + caret_column_tmp = selection.begin; + selection_delete(); + set_caret_column(caret_column_tmp); + insert_text_at_caret(p_data); + grab_focus(); + } else { + insert_text_at_caret(p_data); + grab_focus(); + } + select(caret_column_tmp, caret_column); + if (!text_changed_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed"); + } + text_changed_dirty = true; + } + update(); } } Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const { - if (!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) { + if ((!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) || (!is_editable() && (!is_selecting_enabled() || text.is_empty()))) { return CURSOR_ARROW; } return Control::get_cursor_shape(p_pos); @@ -563,8 +694,8 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const { if (!clear_button_enabled || !has_point(p_pos)) { return false; } - Ref<Texture2D> icon = Control::get_theme_icon("clear"); - int x_ofs = get_theme_stylebox("normal")->get_offset().x; + Ref<Texture2D> icon = Control::get_theme_icon(SNAME("clear")); + int x_ofs = get_theme_stylebox(SNAME("normal"))->get_margin(SIDE_RIGHT); return p_pos.x > get_size().width - icon->get_width() - x_ofs; } @@ -573,8 +704,8 @@ void LineEdit::_notification(int p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_ENTER_TREE: { if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) { - cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); - cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); + set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink")); + set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed")); if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) { EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed)); @@ -582,33 +713,39 @@ void LineEdit::_notification(int p_what) { } } break; #endif + case NOTIFICATION_RESIZED: { _fit_to_width(); scroll_offset = 0; - set_cursor_position(get_cursor_position()); + set_caret_column(get_caret_column()); } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { _shape(); update(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: { - placeholder_translated = tr(placeholder); + placeholder_translated = atr(placeholder); _shape(); update(); } break; + case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; draw_caret = true; update(); } break; + case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { window_has_focus = false; draw_caret = false; update(); } break; + case NOTIFICATION_DRAW: { - if ((!has_focus() && !menu->has_focus() && !caret_force_displayed) || !window_has_focus) { + if ((!has_focus() && !(menu && menu->has_focus()) && !caret_force_displayed) || !window_has_focus) { draw_caret = false; } @@ -621,41 +758,43 @@ void LineEdit::_notification(int p_what) { RID ci = get_canvas_item(); - Ref<StyleBox> style = get_theme_stylebox("normal"); + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); if (!is_editable()) { - style = get_theme_stylebox("read_only"); + style = get_theme_stylebox(SNAME("read_only")); draw_caret = false; } - Ref<Font> font = get_theme_font("font"); + Ref<Font> font = get_theme_font(SNAME("font")); - style->draw(ci, Rect2(Point2(), size)); + if (!flat) { + style->draw(ci, Rect2(Point2(), size)); + } if (has_focus()) { - get_theme_stylebox("focus")->draw(ci, Rect2(Point2(), size)); + get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size)); } int x_ofs = 0; bool using_placeholder = text.is_empty() && ime_text.is_empty(); float text_width = TS->shaped_text_get_size(text_rid).x; - float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM); + float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); - switch (align) { - case ALIGN_FILL: - case ALIGN_LEFT: { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width))); } else { x_ofs = style->get_offset().x; } } break; - case ALIGN_CENTER: { + case HORIZONTAL_ALIGNMENT_CENTER: { if (scroll_offset != 0) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2); } } break; - case ALIGN_RIGHT: { + case HORIZONTAL_ALIGNMENT_RIGHT: { if (rtl) { x_ofs = style->get_offset().x; } else { @@ -669,31 +808,31 @@ void LineEdit::_notification(int p_what) { int y_area = height - style->get_minimum_size().height; int y_ofs = style->get_offset().y + (y_area - text_height) / 2; - Color selection_color = get_theme_color("selection_color"); - Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_uneditable_color"); - Color font_selected_color = get_theme_color("font_selected_color"); - Color cursor_color = get_theme_color("cursor_color"); + Color selection_color = get_theme_color(SNAME("selection_color")); + Color font_color = get_theme_color(is_editable() ? SNAME("font_color") : SNAME("font_uneditable_color")); + Color font_selected_color = get_theme_color(SNAME("font_selected_color")); + Color caret_color = get_theme_color(SNAME("caret_color")); // Draw placeholder color. if (using_placeholder) { - font_color.a *= placeholder_alpha; + font_color = get_theme_color(SNAME("font_placeholder_color")); } bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9); if (display_clear_icon) { if (clear_button_status.press_attempt && clear_button_status.pressing_inside) { - color_icon = get_theme_color("clear_button_color_pressed"); + color_icon = get_theme_color(SNAME("clear_button_color_pressed")); } else { - color_icon = get_theme_color("clear_button_color"); + color_icon = get_theme_color(SNAME("clear_button_color")); } } r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); - if (align == ALIGN_CENTER) { + if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (scroll_offset == 0) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } @@ -704,12 +843,6 @@ void LineEdit::_notification(int p_what) { ofs_max -= r_icon->get_width(); } -#ifdef TOOLS_ENABLED - int caret_width = Math::round(EDSCALE); -#else - int caret_width = 1; -#endif - // Draw selections rects. Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs); if (selection.enabled) { @@ -728,14 +861,13 @@ void LineEdit::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color); } } - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(text_rid); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); + const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid); + int gl_size = TS->shaped_text_get_glyph_count(text_rid); // Draw text. ofs.y += TS->shaped_text_get_ascent(text_rid); - Color font_outline_color = get_theme_color("font_outline_color"); - int outline_size = get_theme_constant("outline_size"); + Color font_outline_color = get_theme_color(SNAME("font_outline_color")); + int outline_size = get_theme_constant(SNAME("outline_size")); if (outline_size > 0 && font_outline_color.a > 0) { Vector2 oofs = ofs; for (int i = 0; i < gl_size; i++) { @@ -771,46 +903,47 @@ void LineEdit::_notification(int p_what) { // Draw carets. ofs.x = x_ofs + scroll_offset; - if (draw_caret) { + if (draw_caret || drag_caret_force_displayed) { + // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1). + const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale()); + if (ime_text.length() == 0) { // Normal caret. - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; - TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column); - if (l_caret == Rect2() && t_caret == Rect2()) { + if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) { // No carets, add one at the start. - int h = get_theme_font("font")->get_height(get_theme_font_size("font_size")); + int h = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size"))); int y = style->get_offset().y + (y_area - h) / 2; if (rtl) { - l_dir = TextServer::DIRECTION_RTL; - l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); + caret.l_dir = TextServer::DIRECTION_RTL; + caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); } else { - l_dir = TextServer::DIRECTION_LTR; - l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); + caret.l_dir = TextServer::DIRECTION_LTR; + caret.l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); } - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color); } else { - if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { + if (caret.l_caret != Rect2() && 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(caret.l_caret.position.x - 3 * caret_width, caret.l_caret.position.y, 6 * caret_width, caret_width); trect.position += ofs; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color); } - l_caret.position += ofs; - l_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + caret.l_caret.position += ofs; + caret.l_caret.size.x = caret_width; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color); - t_caret.position += ofs; - t_caret.size.x = caret_width; + caret.t_caret.position += ofs; + caret.t_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.t_caret, caret_color); } } else { { // IME intermediate text range. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length()); + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length()); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { @@ -823,12 +956,12 @@ void LineEdit::_notification(int p_what) { rect.size.x = ofs_max - rect.position.x; } rect.size.y = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } { // IME caret. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y); + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { @@ -841,7 +974,7 @@ void LineEdit::_notification(int p_what) { rect.size.x = ofs_max - rect.position.x; } rect.size.y = caret_width * 3; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } } @@ -854,6 +987,7 @@ void LineEdit::_notification(int p_what) { } } } break; + case NOTIFICATION_FOCUS_ENTER: { if (!caret_force_displayed) { if (caret_blink_enabled) { @@ -867,12 +1001,13 @@ void LineEdit::_notification(int p_what) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height; - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); + Point2 caret_column = Point2(get_caret_column(), 1) * get_minimum_size().height; + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + caret_column, get_viewport()->get_window_id()); } show_virtual_keyboard(); } break; + case NOTIFICATION_FOCUS_EXIT: { if (caret_blink_enabled && !caret_force_displayed) { caret_blink_timer->stop(); @@ -885,23 +1020,48 @@ void LineEdit::_notification(int p_what) { ime_text = ""; ime_selection = Point2(); _shape(); - set_cursor_position(cursor_pos); // Update scroll_offset + set_caret_column(caret_column); // Update scroll_offset 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()) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); _shape(); - set_cursor_position(cursor_pos); // Update scroll_offset + set_caret_column(caret_column); // Update scroll_offset update(); } } break; + + case NOTIFICATION_DRAG_BEGIN: { + drag_action = true; + } break; + + case 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)) { + selection_delete(); + } else if (deselect_on_focus_loss_enabled) { + deselect(); + } + } + } else { + selection.drag_attempt = false; + } + drag_action = false; + drag_caret_force_displayed = false; + } break; } } @@ -926,12 +1086,12 @@ void LineEdit::paste_text() { // Strip escape characters like \n and \t as they can't be displayed on LineEdit. String paste_buffer = DisplayServer::get_singleton()->clipboard_get().strip_escapes(); - if (paste_buffer != "") { + if (!paste_buffer.is_empty()) { int prev_len = text.length(); if (selection.enabled) { selection_delete(); } - append_at_cursor(paste_buffer); + insert_text_at_caret(paste_buffer); if (!text_changed_dirty) { if (is_inside_tree() && text.length() != prev_len) { @@ -942,6 +1102,17 @@ void LineEdit::paste_text() { } } +bool LineEdit::has_undo() const { + if (undo_stack_pos == nullptr) { + return undo_stack.size() > 1; + } + return undo_stack_pos != undo_stack.front(); +} + +bool LineEdit::has_redo() const { + return undo_stack_pos != nullptr && undo_stack_pos != undo_stack.back(); +} + void LineEdit::undo() { if (!editable) { return; @@ -955,11 +1126,14 @@ void LineEdit::undo() { } else if (undo_stack_pos == undo_stack.front()) { return; } + + deselect(); + undo_stack_pos = undo_stack_pos->prev(); TextOperation op = undo_stack_pos->get(); text = op.text; scroll_offset = op.scroll_offset; - set_cursor_position(op.cursor_pos); + set_caret_column(op.caret_column); _shape(); _emit_text_change(); @@ -976,11 +1150,14 @@ void LineEdit::redo() { if (undo_stack_pos == undo_stack.back()) { return; } + + deselect(); + undo_stack_pos = undo_stack_pos->next(); TextOperation op = undo_stack_pos->get(); text = op.text; scroll_offset = op.scroll_offset; - set_cursor_position(op.cursor_pos); + set_caret_column(op.caret_column); _shape(); _emit_text_change(); @@ -988,7 +1165,7 @@ void LineEdit::redo() { void LineEdit::shift_selection_check_pre(bool p_shift) { if (!selection.enabled && p_shift) { - selection.cursor_start = cursor_pos; + selection.start_column = caret_column; } if (!p_shift) { deselect(); @@ -997,33 +1174,33 @@ void LineEdit::shift_selection_check_pre(bool p_shift) { void LineEdit::shift_selection_check_post(bool p_shift) { if (p_shift) { - selection_fill_at_cursor(); + selection_fill_at_caret(); } } -void LineEdit::set_cursor_at_pixel_pos(int p_x) { - Ref<StyleBox> style = get_theme_stylebox("normal"); +void LineEdit::set_caret_at_pixel_pos(int p_x) { + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); bool rtl = is_layout_rtl(); int x_ofs = 0; float text_width = TS->shaped_text_get_size(text_rid).x; - switch (align) { - case ALIGN_FILL: - case ALIGN_LEFT: { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); } else { x_ofs = style->get_offset().x; } } break; - case ALIGN_CENTER: { + case HORIZONTAL_ALIGNMENT_CENTER: { if (scroll_offset != 0) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); } } break; - case ALIGN_RIGHT: { + case HORIZONTAL_ALIGNMENT_RIGHT: { if (rtl) { x_ofs = style->get_offset().x; } else { @@ -1035,8 +1212,8 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; - if (align == ALIGN_CENTER) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (scroll_offset == 0) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } @@ -1046,32 +1223,32 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset); - set_cursor_position(ofs); + set_caret_column(ofs); } -Vector2i LineEdit::get_cursor_pixel_pos() { - Ref<StyleBox> style = get_theme_stylebox("normal"); +Vector2i LineEdit::get_caret_pixel_pos() { + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); bool rtl = is_layout_rtl(); int x_ofs = 0; float text_width = TS->shaped_text_get_size(text_rid).x; - switch (align) { - case ALIGN_FILL: - case ALIGN_LEFT: { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); } else { x_ofs = style->get_offset().x; } } break; - case ALIGN_CENTER: { + case HORIZONTAL_ALIGNMENT_CENTER: { if (scroll_offset != 0) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); } } break; - case ALIGN_RIGHT: { + case HORIZONTAL_ALIGNMENT_RIGHT: { if (rtl) { x_ofs = style->get_offset().x; } else { @@ -1083,8 +1260,8 @@ Vector2i LineEdit::get_cursor_pixel_pos() { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; - if (align == ALIGN_CENTER) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (scroll_offset == 0) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } @@ -1094,32 +1271,31 @@ Vector2i LineEdit::get_cursor_pixel_pos() { } Vector2i ret; - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; + CaretInfo caret; // Get position of the start of caret. if (ime_text.length() != 0 && ime_selection.x != 0) { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x); } else { - TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column); } - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - ret.x = x_ofs + l_caret.position.x + scroll_offset; + if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) { + ret.x = x_ofs + caret.l_caret.position.x + scroll_offset; } else { - ret.x = x_ofs + t_caret.position.x + scroll_offset; + ret.x = x_ofs + caret.t_caret.position.x + scroll_offset; } // Get position of the end of caret. if (ime_text.length() != 0) { if (ime_selection.y != 0) { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y); } else { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size()); } - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - ret.y = x_ofs + l_caret.position.x + scroll_offset; + if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) { + ret.y = x_ofs + caret.l_caret.position.x + scroll_offset; } else { - ret.y = x_ofs + t_caret.position.x + scroll_offset; + ret.y = x_ofs + caret.t_caret.position.x + scroll_offset; } } else { ret.y = ret.x; @@ -1128,19 +1304,19 @@ Vector2i LineEdit::get_cursor_pixel_pos() { return ret; } -void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) { - mid_grapheme_caret_enabled = p_enabled; +void LineEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) { + caret_mid_grapheme_enabled = p_enabled; } -bool LineEdit::get_mid_grapheme_caret_enabled() const { - return mid_grapheme_caret_enabled; +bool LineEdit::is_caret_mid_grapheme_enabled() const { + return caret_mid_grapheme_enabled; } -bool LineEdit::cursor_get_blink_enabled() const { +bool LineEdit::is_caret_blink_enabled() const { return caret_blink_enabled; } -void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { +void LineEdit::set_caret_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; if (has_focus() || caret_force_displayed) { @@ -1158,21 +1334,21 @@ void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { notify_property_list_changed(); } -bool LineEdit::cursor_get_force_displayed() const { +bool LineEdit::is_caret_force_displayed() const { return caret_force_displayed; } -void LineEdit::cursor_set_force_displayed(const bool p_enabled) { +void LineEdit::set_caret_force_displayed(const bool p_enabled) { caret_force_displayed = p_enabled; - cursor_set_blink_enabled(caret_blink_enabled); + set_caret_blink_enabled(caret_blink_enabled); update(); } -float LineEdit::cursor_get_blink_speed() const { +float LineEdit::get_caret_blink_speed() const { return caret_blink_timer->get_wait_time(); } -void LineEdit::cursor_set_blink_speed(const float p_speed) { +void LineEdit::set_caret_blink_speed(const float p_speed) { ERR_FAIL_COND(p_speed <= 0); caret_blink_timer->set_wait_time(p_speed); } @@ -1196,14 +1372,14 @@ void LineEdit::_toggle_draw_caret() { } void LineEdit::delete_char() { - if ((text.length() <= 0) || (cursor_pos == 0)) { + if ((text.length() <= 0) || (caret_column == 0)) { return; } - text.erase(cursor_pos - 1, 1); + text = text.left(caret_column - 1) + text.substr(caret_column); _shape(); - set_cursor_position(get_cursor_position() - 1); + set_caret_column(get_caret_column() - 1); _text_changed(); } @@ -1212,13 +1388,13 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { ERR_FAIL_COND_MSG(p_from_column < 0 || p_from_column > p_to_column || p_to_column > text.length(), vformat("Positional parameters (from: %d, to: %d) are inverted or outside the text length (%d).", p_from_column, p_to_column, text.length())); - text.erase(p_from_column, p_to_column - p_from_column); + text = text.left(p_from_column) + text.substr(p_to_column); _shape(); - cursor_pos -= CLAMP(cursor_pos - p_from_column, 0, p_to_column - p_from_column); + caret_column -= CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column); - if (cursor_pos >= text.length()) { - cursor_pos = text.length(); + if (caret_column >= text.length()) { + caret_column = text.length(); } if (!text_changed_dirty) { @@ -1231,10 +1407,11 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { void LineEdit::set_text(String p_text) { clear_internal(); - append_at_cursor(p_text); + insert_text_at_caret(p_text); + _create_undo_state(); update(); - cursor_pos = 0; + caret_column = 0; scroll_offset = 0; } @@ -1247,10 +1424,12 @@ void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { } _shape(); - 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); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); + if (menu_dir) { + 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); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); + } update(); } } @@ -1297,7 +1476,9 @@ String LineEdit::get_language() const { void LineEdit::set_draw_control_chars(bool p_draw_control_chars) { if (draw_control_chars != p_draw_control_chars) { draw_control_chars = p_draw_control_chars; - menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); + if (menu && menu->get_item_index(MENU_DISPLAY_UCC) >= 0) { + menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); + } _shape(); update(); } @@ -1307,7 +1488,7 @@ bool LineEdit::get_draw_control_chars() const { return draw_control_chars; } -void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void LineEdit::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; _shape(); @@ -1315,7 +1496,7 @@ void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p } } -Control::StructuredTextParser LineEdit::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser LineEdit::get_structured_text_bidi_override() const { return st_parser; } @@ -1344,7 +1525,7 @@ void LineEdit::show_virtual_keyboard() { if (selection.enabled) { DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end); } else { - DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos); + DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, caret_column); } } } @@ -1355,7 +1536,7 @@ String LineEdit::get_text() const { void LineEdit::set_placeholder(String p_text) { placeholder = p_text; - placeholder_translated = tr(placeholder); + placeholder_translated = atr(placeholder); _shape(); update(); } @@ -1364,25 +1545,16 @@ String LineEdit::get_placeholder() const { return placeholder; } -void LineEdit::set_placeholder_alpha(float p_alpha) { - placeholder_alpha = p_alpha; - update(); -} - -float LineEdit::get_placeholder_alpha() const { - return placeholder_alpha; -} - -void LineEdit::set_cursor_position(int p_pos) { - if (p_pos > (int)text.length()) { - p_pos = text.length(); +void LineEdit::set_caret_column(int p_column) { + if (p_column > (int)text.length()) { + p_column = text.length(); } - if (p_pos < 0) { - p_pos = 0; + if (p_column < 0) { + p_column = 0; } - cursor_pos = p_pos; + caret_column = p_column; // Fit to window. @@ -1391,28 +1563,28 @@ void LineEdit::set_cursor_position(int p_pos) { return; } - Ref<StyleBox> style = get_theme_stylebox("normal"); + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); bool rtl = is_layout_rtl(); int x_ofs = 0; float text_width = TS->shaped_text_get_size(text_rid).x; - switch (align) { - case ALIGN_FILL: - case ALIGN_LEFT: { + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); } else { x_ofs = style->get_offset().x; } } break; - case ALIGN_CENTER: { + case HORIZONTAL_ALIGNMENT_CENTER: { if (scroll_offset != 0) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); } } break; - case ALIGN_RIGHT: { + case HORIZONTAL_ALIGNMENT_RIGHT: { if (rtl) { x_ofs = style->get_offset().x; } else { @@ -1425,8 +1597,8 @@ void LineEdit::set_cursor_position(int p_pos) { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; - if (align == ALIGN_CENTER) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (scroll_offset == 0) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } @@ -1437,7 +1609,7 @@ void LineEdit::set_cursor_position(int p_pos) { } // Note: Use two coordinates to fit IME input range. - Vector2i primary_catret_offset = get_cursor_pixel_pos(); + Vector2i primary_catret_offset = get_caret_pixel_pos(); if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) { scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y)); @@ -1449,8 +1621,8 @@ void LineEdit::set_cursor_position(int p_pos) { update(); } -int LineEdit::get_cursor_position() const { - return cursor_pos; +int LineEdit::get_caret_column() const { + return caret_column; } void LineEdit::set_scroll_offset(int p_pos) { @@ -1464,26 +1636,30 @@ int LineEdit::get_scroll_offset() const { return scroll_offset; } -void LineEdit::append_at_cursor(String p_text) { - if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) { - String pre = text.substr(0, cursor_pos); - String post = text.substr(cursor_pos, text.length() - cursor_pos); - text = pre + p_text + post; - _shape(); - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, cursor_pos, cursor_pos + p_text.length()); - if (dir != TextServer::DIRECTION_AUTO) { - input_direction = (TextDirection)dir; +void LineEdit::insert_text_at_caret(String p_text) { + if (max_length > 0) { + // Truncate text to append to fit in max_length, if needed. + int available_chars = max_length - text.length(); + if (p_text.length() > available_chars) { + emit_signal(SNAME("text_change_rejected"), p_text.substr(available_chars)); + p_text = p_text.substr(0, available_chars); } - set_cursor_position(cursor_pos + p_text.length()); - } else { - emit_signal("text_change_rejected"); } + String pre = text.substr(0, caret_column); + String post = text.substr(caret_column, text.length() - caret_column); + text = pre + p_text + post; + _shape(); + TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text_rid, caret_column, caret_column + p_text.length()); + if (dir != TextServer::DIRECTION_AUTO) { + input_direction = (TextDirection)dir; + } + set_caret_column(caret_column + p_text.length()); } void LineEdit::clear_internal() { deselect(); _clear_undo_stack(); - cursor_pos = 0; + caret_column = 0; scroll_offset = 0; undo_text = ""; text = ""; @@ -1492,31 +1668,35 @@ void LineEdit::clear_internal() { } Size2 LineEdit::get_minimum_size() const { - Ref<StyleBox> style = get_theme_stylebox("normal"); - Ref<Font> font = get_theme_font("font"); - int font_size = get_theme_font_size("font_size"); + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<Font> font = get_theme_font(SNAME("font")); + int font_size = get_theme_font_size(SNAME("font_size")); Size2 min_size; // Minimum size of text. - int em_space_size = font->get_char_size('M', 0, font_size).x; - min_size.width = get_theme_constant("minimum_character_width") * em_space_size; + float em_space_size = font->get_char_size('M', 0, font_size).x; + min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size; if (expand_to_text_length) { - // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. + // Add a space because some fonts are too exact, and because caret needs a bit more when at the end. min_size.width = MAX(min_size.width, full_width + em_space_size); } - min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size)); + min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM), font->get_height(font_size)); // Take icons into account. - bool using_placeholder = text.is_empty() && ime_text.is_empty(); - bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; - if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; - min_size.width += r_icon->get_width(); - min_size.height = MAX(min_size.height, r_icon->get_height()); + int icon_max_width = 0; + if (right_icon.is_valid()) { + min_size.height = MAX(min_size.height, right_icon->get_height()); + icon_max_width = right_icon->get_width(); } + if (clear_button_enabled) { + Ref<Texture2D> clear_icon = Control::get_theme_icon(SNAME("clear")); + min_size.height = MAX(min_size.height, clear_icon->get_height()); + icon_max_width = MAX(icon_max_width, clear_icon->get_width()); + } + min_size.width += icon_max_width; return style->get_minimum_size() + min_size; } @@ -1524,13 +1704,27 @@ Size2 LineEdit::get_minimum_size() const { void LineEdit::deselect() { selection.begin = 0; selection.end = 0; - selection.cursor_start = 0; + selection.start_column = 0; selection.enabled = false; selection.creating = false; - selection.doubleclick = false; + selection.double_click = false; update(); } +bool LineEdit::has_selection() const { + return selection.enabled; +} + +int LineEdit::get_selection_from_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.begin; +} + +int LineEdit::get_selection_to_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.end; +} + void LineEdit::selection_delete() { if (selection.enabled) { delete_text(selection.begin, selection.end); @@ -1549,13 +1743,13 @@ int LineEdit::get_max_length() const { return max_length; } -void LineEdit::selection_fill_at_cursor() { +void LineEdit::selection_fill_at_caret() { if (!selecting_enabled) { return; } - selection.begin = cursor_pos; - selection.end = selection.cursor_start; + selection.begin = caret_column; + selection.end = selection.start_column; if (selection.end < selection.begin) { int aux = selection.end; @@ -1587,9 +1781,8 @@ void LineEdit::set_editable(bool p_editable) { } editable = p_editable; - _generate_context_menu(); - minimum_size_changed(); + update_minimum_size(); update(); } @@ -1654,7 +1847,7 @@ void LineEdit::select(int p_from, int p_to) { selection.begin = p_from; selection.end = p_to; selection.creating = false; - selection.doubleclick = false; + selection.double_click = false; update(); } @@ -1712,82 +1905,82 @@ void LineEdit::menu_option(int p_option) { } break; case MENU_INSERT_LRM: { if (editable) { - append_at_cursor(String::chr(0x200E)); + insert_text_at_caret(String::chr(0x200E)); } } break; case MENU_INSERT_RLM: { if (editable) { - append_at_cursor(String::chr(0x200F)); + insert_text_at_caret(String::chr(0x200F)); } } break; case MENU_INSERT_LRE: { if (editable) { - append_at_cursor(String::chr(0x202A)); + insert_text_at_caret(String::chr(0x202A)); } } break; case MENU_INSERT_RLE: { if (editable) { - append_at_cursor(String::chr(0x202B)); + insert_text_at_caret(String::chr(0x202B)); } } break; case MENU_INSERT_LRO: { if (editable) { - append_at_cursor(String::chr(0x202D)); + insert_text_at_caret(String::chr(0x202D)); } } break; case MENU_INSERT_RLO: { if (editable) { - append_at_cursor(String::chr(0x202E)); + insert_text_at_caret(String::chr(0x202E)); } } break; case MENU_INSERT_PDF: { if (editable) { - append_at_cursor(String::chr(0x202C)); + insert_text_at_caret(String::chr(0x202C)); } } break; case MENU_INSERT_ALM: { if (editable) { - append_at_cursor(String::chr(0x061C)); + insert_text_at_caret(String::chr(0x061C)); } } break; case MENU_INSERT_LRI: { if (editable) { - append_at_cursor(String::chr(0x2066)); + insert_text_at_caret(String::chr(0x2066)); } } break; case MENU_INSERT_RLI: { if (editable) { - append_at_cursor(String::chr(0x2067)); + insert_text_at_caret(String::chr(0x2067)); } } break; case MENU_INSERT_FSI: { if (editable) { - append_at_cursor(String::chr(0x2068)); + insert_text_at_caret(String::chr(0x2068)); } } break; case MENU_INSERT_PDI: { if (editable) { - append_at_cursor(String::chr(0x2069)); + insert_text_at_caret(String::chr(0x2069)); } } break; case MENU_INSERT_ZWJ: { if (editable) { - append_at_cursor(String::chr(0x200D)); + insert_text_at_caret(String::chr(0x200D)); } } break; case MENU_INSERT_ZWNJ: { if (editable) { - append_at_cursor(String::chr(0x200C)); + insert_text_at_caret(String::chr(0x200C)); } } break; case MENU_INSERT_WJ: { if (editable) { - append_at_cursor(String::chr(0x2060)); + insert_text_at_caret(String::chr(0x2060)); } } break; case MENU_INSERT_SHY: { if (editable) { - append_at_cursor(String::chr(0x00AD)); + insert_text_at_caret(String::chr(0x00AD)); } } } @@ -1801,24 +1994,29 @@ bool LineEdit::is_context_menu_enabled() { return context_menu_enabled; } +bool LineEdit::is_menu_visible() const { + return menu && menu->is_visible(); +} + PopupMenu *LineEdit::get_menu() const { + const_cast<LineEdit *>(this)->_ensure_menu(); return menu; } void LineEdit::_editor_settings_changed() { #ifdef TOOLS_ENABLED - cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); - cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); + set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink")); + set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed")); #endif } -void LineEdit::set_expand_to_text_length(bool p_enabled) { +void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) { expand_to_text_length = p_enabled; - minimum_size_changed(); - set_cursor_position(cursor_pos); + update_minimum_size(); + set_caret_column(caret_column); } -bool LineEdit::get_expand_to_text_length() const { +bool LineEdit::is_expand_to_text_length_enabled() const { return expand_to_text_length; } @@ -1828,7 +2026,7 @@ void LineEdit::set_clear_button_enabled(bool p_enabled) { } clear_button_enabled = p_enabled; _fit_to_width(); - minimum_size_changed(); + update_minimum_size(); update(); } @@ -1838,8 +2036,6 @@ bool LineEdit::is_clear_button_enabled() const { void LineEdit::set_shortcut_keys_enabled(bool p_enabled) { shortcut_keys_enabled = p_enabled; - - _generate_context_menu(); } bool LineEdit::is_shortcut_keys_enabled() const { @@ -1854,27 +2050,44 @@ bool LineEdit::is_virtual_keyboard_enabled() const { return virtual_keyboard_enabled; } +void LineEdit::set_middle_mouse_paste_enabled(bool p_enabled) { + middle_mouse_paste_enabled = p_enabled; +} + +bool LineEdit::is_middle_mouse_paste_enabled() const { + return middle_mouse_paste_enabled; +} + void LineEdit::set_selecting_enabled(bool p_enabled) { selecting_enabled = p_enabled; if (!selecting_enabled) { deselect(); } - - _generate_context_menu(); } bool LineEdit::is_selecting_enabled() const { return selecting_enabled; } +void LineEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + deselect_on_focus_loss_enabled = p_enabled; + if (p_enabled && selection.enabled && !has_focus()) { + deselect(); + } +} + +bool LineEdit::is_deselect_on_focus_loss_enabled() const { + return deselect_on_focus_loss_enabled; +} + void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) { if (right_icon == p_icon) { return; } right_icon = p_icon; _fit_to_width(); - minimum_size_changed(); + update_minimum_size(); update(); } @@ -1882,13 +2095,24 @@ Ref<Texture2D> LineEdit::get_right_icon() { return right_icon; } +void LineEdit::set_flat(bool p_enabled) { + if (flat != p_enabled) { + flat = p_enabled; + update(); + } +} + +bool LineEdit::is_flat() const { + return flat; +} + void LineEdit::_text_changed() { _emit_text_change(); _clear_redo(); } void LineEdit::_emit_text_change() { - emit_signal("text_changed", text); + emit_signal(SNAME("text_changed"), text); text_changed_dirty = false; } @@ -1897,13 +2121,13 @@ void LineEdit::_shape() { TS->shaped_text_clear(text_rid); String t; - if (text.length() == 0) { + if (text.length() == 0 && ime_text.length() == 0) { t = placeholder_translated; } else if (pass) { t = secret_character.repeat(text.length() + ime_text.length()); } else { if (ime_text.length() > 0) { - t = text.substr(0, cursor_pos) + ime_text + text.substr(cursor_pos, text.length()); + t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); } else { t = text; } @@ -1915,9 +2139,10 @@ void LineEdit::_shape() { } TS->shaped_text_set_preserve_control(text_rid, draw_control_chars); - const Ref<Font> &font = get_theme_font("font"); - int font_size = get_theme_font_size("font_size"); - TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + const Ref<Font> &font = get_theme_font(SNAME("font")); + int font_size = get_theme_font_size(SNAME("font_size")); + ERR_FAIL_COND(font.is_null()); + TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t)); full_width = TS->shaped_text_get_size(text_rid).x; @@ -1926,18 +2151,18 @@ void LineEdit::_shape() { Size2 size = TS->shaped_text_get_size(text_rid); if ((expand_to_text_length && old_size.x != size.x) || (old_size.y != size.y)) { - minimum_size_changed(); + update_minimum_size(); } } void LineEdit::_fit_to_width() { - if (align == ALIGN_FILL) { - Ref<StyleBox> style = get_theme_stylebox("normal"); + if (alignment == HORIZONTAL_ALIGNMENT_FILL) { + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT); bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; t_width -= r_icon->get_width(); } TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width)); @@ -1968,71 +2193,42 @@ void LineEdit::_clear_undo_stack() { void LineEdit::_create_undo_state() { TextOperation op; op.text = text; - op.cursor_pos = cursor_pos; + op.caret_column = caret_column; op.scroll_offset = scroll_offset; undo_stack.push_back(op); } -int LineEdit::_get_menu_action_accelerator(const String &p_action) { +Key LineEdit::_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(); } } -void LineEdit::_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("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0); - if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0); - } - 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); - } - 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_separator(); - menu->add_submenu_item(RTR("Text writing direction"), "DirMenu"); - menu->add_separator(); - menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC); - if (editable) { - menu->add_submenu_item(RTR("Insert control character"), "CTLMenu"); - } -} - bool LineEdit::_set(const StringName &p_name, const Variant &p_value) { String str = p_name; if (str.begins_with("opentype_features/")) { String name = str.get_slicec('/', 1); int32_t tag = TS->name_to_tag(name); - double value = p_value; + int value = p_value; if (value == -1) { if (opentype_features.has(tag)) { opentype_features.erase(tag); @@ -2040,7 +2236,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) { update(); } } else { - if ((double)opentype_features[tag] != value) { + if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { opentype_features[tag] = value; _shape(); update(); @@ -2072,28 +2268,30 @@ bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const { void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const { for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); } p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); } void LineEdit::_validate_property(PropertyInfo &property) const { if (!caret_blink_enabled && property.name == "caret_blink_speed") { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed); - ClassDB::bind_method(D_METHOD("set_align", "align"), &LineEdit::set_align); - ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align); + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment); + ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment); - ClassDB::bind_method(D_METHOD("_gui_input"), &LineEdit::_gui_input); ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear); ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); + ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection); + ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column); + ClassDB::bind_method(D_METHOD("get_selection_to_column"), &LineEdit::get_selection_to_column); ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text); ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars); @@ -2111,25 +2309,23 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LineEdit::get_structured_text_bidi_override_options); ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder); ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder); - ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha); - ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha); - ClassDB::bind_method(D_METHOD("set_cursor_position", "position"), &LineEdit::set_cursor_position); - ClassDB::bind_method(D_METHOD("get_cursor_position"), &LineEdit::get_cursor_position); + ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column); + ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column); ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset); - ClassDB::bind_method(D_METHOD("set_expand_to_text_length", "enabled"), &LineEdit::set_expand_to_text_length); - ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length); - ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled); - ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled); - ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &LineEdit::set_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &LineEdit::get_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed); - ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed); - ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed); - ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &LineEdit::cursor_get_blink_speed); + ClassDB::bind_method(D_METHOD("set_expand_to_text_length_enabled", "enabled"), &LineEdit::set_expand_to_text_length_enabled); + ClassDB::bind_method(D_METHOD("is_expand_to_text_length_enabled"), &LineEdit::is_expand_to_text_length_enabled); + ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enabled"), &LineEdit::set_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &LineEdit::is_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &LineEdit::set_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed); + ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed); + ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &LineEdit::set_caret_blink_speed); + ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &LineEdit::get_caret_blink_speed); ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length); ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length); - ClassDB::bind_method(D_METHOD("append_at_cursor", "text"), &LineEdit::append_at_cursor); - ClassDB::bind_method(D_METHOD("delete_char_at_cursor"), &LineEdit::delete_char); + ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret); + ClassDB::bind_method(D_METHOD("delete_char_at_caret"), &LineEdit::delete_char); ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text); ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable); @@ -2139,6 +2335,7 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_secret_character"), &LineEdit::get_secret_character); ClassDB::bind_method(D_METHOD("menu_option", "option"), &LineEdit::menu_option); ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu); + ClassDB::bind_method(D_METHOD("is_menu_visible"), &LineEdit::is_menu_visible); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled); ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &LineEdit::set_virtual_keyboard_enabled); @@ -2147,19 +2344,20 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled); ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &LineEdit::set_shortcut_keys_enabled); ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_middle_mouse_paste_enabled", "enable"), &LineEdit::set_middle_mouse_paste_enabled); + ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &LineEdit::is_middle_mouse_paste_enabled); ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled); ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled); + ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &LineEdit::set_deselect_on_focus_loss_enabled); + ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &LineEdit::is_deselect_on_focus_loss_enabled); ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon); ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); + ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &LineEdit::set_flat); + ClassDB::bind_method(D_METHOD("is_flat"), &LineEdit::is_flat); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); - ADD_SIGNAL(MethodInfo("text_change_rejected")); - ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text"))); - - BIND_ENUM_CONSTANT(ALIGN_LEFT); - BIND_ENUM_CONSTANT(ALIGN_CENTER); - BIND_ENUM_CONSTANT(ALIGN_RIGHT); - BIND_ENUM_CONSTANT(ALIGN_FILL); + ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring"))); + ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text"))); BIND_ENUM_CONSTANT(MENU_CUT); BIND_ENUM_CONSTANT(MENU_COPY); @@ -2192,36 +2390,115 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_MAX); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_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::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); - ADD_GROUP("Placeholder", "placeholder_"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); ADD_GROUP("Caret", "caret_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); +} + +void LineEdit::_ensure_menu() { + if (!menu) { + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + + 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->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_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_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->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); + + menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + } + + // Reorganize context menu. + menu->clear(); + if (editable) { + 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") : Key::NONE); + if (editable) { + 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") : 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") : 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_separator(); + 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_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); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); + + if (editable) { + menu->set_item_disabled(menu->get_item_index(MENU_UNDO), !has_undo()); + menu->set_item_disabled(menu->get_item_index(MENU_REDO), !has_redo()); + } } -LineEdit::LineEdit() { +LineEdit::LineEdit(const String &p_placeholder) { text_rid = TS->create_shaped_text(); _create_undo_state(); @@ -2229,53 +2506,19 @@ LineEdit::LineEdit() { set_focus_mode(FOCUS_ALL); set_default_cursor_shape(CURSOR_IBEAM); set_mouse_filter(MOUSE_FILTER_STOP); + set_process_unhandled_key_input(true); caret_blink_timer = memnew(Timer); - add_child(caret_blink_timer); + add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); - cursor_set_blink_enabled(false); - - menu = memnew(PopupMenu); - add_child(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->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), true); - menu->add_child(menu_dir); - - 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_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_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->add_child(menu_ctl); + set_caret_blink_enabled(false); + + set_placeholder(p_placeholder); set_editable(true); // Initialise to opposite first, so we get past the early-out in set_editable. - menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); } LineEdit::~LineEdit() { - TS->free(text_rid); + TS->free_rid(text_rid); } |