1/**************************************************************************/
2/* line_edit.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "line_edit.h"
32
33#include "core/input/input_map.h"
34#include "core/object/message_queue.h"
35#include "core/os/keyboard.h"
36#include "core/os/os.h"
37#include "core/string/print_string.h"
38#include "core/string/translation.h"
39#include "scene/gui/label.h"
40#include "scene/main/window.h"
41#include "scene/theme/theme_db.h"
42#include "servers/display_server.h"
43#include "servers/text_server.h"
44
45#ifdef TOOLS_ENABLED
46#include "editor/editor_settings.h"
47#endif
48
49void LineEdit::_swap_current_input_direction() {
50 if (input_direction == TEXT_DIRECTION_LTR) {
51 input_direction = TEXT_DIRECTION_RTL;
52 } else {
53 input_direction = TEXT_DIRECTION_LTR;
54 }
55 set_caret_column(get_caret_column());
56 queue_redraw();
57}
58
59void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
60 if (selection.enabled && !p_select) {
61 set_caret_column(selection.begin);
62 deselect();
63 return;
64 }
65
66 shift_selection_check_pre(p_select);
67
68 if (p_move_by_word) {
69 int cc = caret_column;
70
71 PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
72 for (int i = words.size() - 2; i >= 0; i = i - 2) {
73 if (words[i] < cc) {
74 cc = words[i];
75 break;
76 }
77 }
78
79 set_caret_column(cc);
80 } else {
81 if (caret_mid_grapheme_enabled) {
82 set_caret_column(get_caret_column() - 1);
83 } else {
84 set_caret_column(TS->shaped_text_prev_character_pos(text_rid, get_caret_column()));
85 }
86 }
87
88 shift_selection_check_post(p_select);
89 _reset_caret_blink_timer();
90}
91
92void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
93 if (selection.enabled && !p_select) {
94 set_caret_column(selection.end);
95 deselect();
96 return;
97 }
98
99 shift_selection_check_pre(p_select);
100
101 if (p_move_by_word) {
102 int cc = caret_column;
103
104 PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
105 for (int i = 1; i < words.size(); i = i + 2) {
106 if (words[i] > cc) {
107 cc = words[i];
108 break;
109 }
110 }
111
112 set_caret_column(cc);
113 } else {
114 if (caret_mid_grapheme_enabled) {
115 set_caret_column(get_caret_column() + 1);
116 } else {
117 set_caret_column(TS->shaped_text_next_character_pos(text_rid, get_caret_column()));
118 }
119 }
120
121 shift_selection_check_post(p_select);
122 _reset_caret_blink_timer();
123}
124
125void LineEdit::_move_caret_start(bool p_select) {
126 shift_selection_check_pre(p_select);
127 set_caret_column(0);
128 shift_selection_check_post(p_select);
129}
130
131void LineEdit::_move_caret_end(bool p_select) {
132 shift_selection_check_pre(p_select);
133 set_caret_column(text.length());
134 shift_selection_check_post(p_select);
135}
136
137void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
138 if (!editable) {
139 return;
140 }
141
142 if (p_all_to_left) {
143 deselect();
144 text = text.substr(0, caret_column);
145 _text_changed();
146 return;
147 }
148
149 if (selection.enabled) {
150 selection_delete();
151 return;
152 }
153
154 if (p_word) {
155 int cc = caret_column;
156
157 PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
158 for (int i = words.size() - 2; i >= 0; i = i - 2) {
159 if (words[i] < cc) {
160 cc = words[i];
161 break;
162 }
163 }
164
165 delete_text(cc, caret_column);
166
167 set_caret_column(cc);
168 } else {
169 delete_char();
170 }
171}
172
173void LineEdit::_delete(bool p_word, bool p_all_to_right) {
174 if (!editable) {
175 return;
176 }
177
178 if (p_all_to_right) {
179 deselect();
180 text = text.substr(caret_column, text.length() - caret_column);
181 _shape();
182 set_caret_column(0);
183 _text_changed();
184 return;
185 }
186
187 if (selection.enabled) {
188 selection_delete();
189 return;
190 }
191
192 int text_len = text.length();
193
194 if (caret_column == text_len) {
195 return; // Nothing to do.
196 }
197
198 if (p_word) {
199 int cc = caret_column;
200 PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
201 for (int i = 1; i < words.size(); i = i + 2) {
202 if (words[i] > cc) {
203 cc = words[i];
204 break;
205 }
206 }
207
208 delete_text(caret_column, cc);
209 set_caret_column(caret_column);
210 } else {
211 if (caret_mid_grapheme_enabled) {
212 set_caret_column(caret_column + 1);
213 delete_char();
214 } else {
215 int cc = caret_column;
216 set_caret_column(TS->shaped_text_next_character_pos(text_rid, caret_column));
217 delete_text(cc, caret_column);
218 }
219 }
220}
221
222void LineEdit::unhandled_key_input(const Ref<InputEvent> &p_event) {
223 Ref<InputEventKey> k = p_event;
224
225 if (k.is_valid()) {
226 if (!k->is_pressed()) {
227 return;
228 }
229 // Handle Unicode (with modifiers active, process after shortcuts).
230 if (has_focus() && editable && (k->get_unicode() >= 32)) {
231 selection_delete();
232 char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
233 int prev_len = text.length();
234 insert_text_at_caret(ucodestr);
235 if (text.length() != prev_len) {
236 _text_changed();
237 }
238 accept_event();
239 }
240 }
241}
242
243void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
244 ERR_FAIL_COND(p_event.is_null());
245
246 Ref<InputEventMouseButton> b = p_event;
247
248 if (b.is_valid()) {
249 if (ime_text.length() != 0) {
250 // Ignore mouse clicks in IME input mode.
251 return;
252 }
253 if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
254 _update_context_menu();
255 menu->set_position(get_screen_position() + get_local_mouse_position());
256 menu->reset_size();
257 menu->popup();
258 grab_focus();
259 accept_event();
260 return;
261 }
262
263 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)) {
264 String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes();
265
266 deselect();
267 set_caret_at_pixel_pos(b->get_position().x);
268 if (!paste_buffer.is_empty()) {
269 insert_text_at_caret(paste_buffer);
270
271 if (!text_changed_dirty) {
272 if (is_inside_tree()) {
273 MessageQueue::get_singleton()->push_call(this, "_text_changed");
274 }
275 text_changed_dirty = true;
276 }
277 }
278 grab_focus();
279 accept_event();
280 return;
281 }
282
283 if (b->get_button_index() != MouseButton::LEFT) {
284 return;
285 }
286
287 _reset_caret_blink_timer();
288 if (b->is_pressed()) {
289 accept_event(); // Don't pass event further when clicked on text field.
290 if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) {
291 clear_button_status.press_attempt = true;
292 clear_button_status.pressing_inside = true;
293 queue_redraw();
294 return;
295 }
296
297 if (b->is_shift_pressed()) {
298 shift_selection_check_pre(true);
299 }
300
301 set_caret_at_pixel_pos(b->get_position().x);
302
303 if (b->is_shift_pressed()) {
304 selection_fill_at_caret();
305 selection.creating = true;
306
307 } else {
308 if (selecting_enabled) {
309 const int triple_click_timeout = 600;
310 const int triple_click_tolerance = 5;
311 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;
312
313 if (is_triple_click && text.length()) {
314 // Triple-click select all.
315 selection.enabled = true;
316 selection.begin = 0;
317 selection.end = text.length();
318 selection.double_click = true;
319 last_dblclk = 0;
320 set_caret_column(selection.begin);
321 if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
322 DisplayServer::get_singleton()->clipboard_set_primary(text);
323 }
324 } else if (b->is_double_click()) {
325 // Double-click select word.
326 last_dblclk = OS::get_singleton()->get_ticks_msec();
327 last_dblclk_pos = b->get_position();
328 PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
329 for (int i = 0; i < words.size(); i = i + 2) {
330 if ((words[i] < caret_column && words[i + 1] > caret_column) || (i == words.size() - 2 && caret_column == words[i + 1])) {
331 selection.enabled = true;
332 selection.begin = words[i];
333 selection.end = words[i + 1];
334 selection.double_click = true;
335 set_caret_column(selection.end);
336 break;
337 }
338 }
339 if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
340 DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
341 }
342 }
343 }
344
345 selection.drag_attempt = false;
346 if (!selection.double_click) {
347 bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end;
348 if (drag_and_drop_selection_enabled && is_inside_sel) {
349 selection.drag_attempt = true;
350 } else {
351 deselect();
352 selection.start_column = caret_column;
353 selection.creating = true;
354 }
355 }
356 }
357
358 queue_redraw();
359
360 } else {
361 if (selection.enabled && !pass && b->get_button_index() == MouseButton::LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
362 DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
363 }
364 if (!text.is_empty() && is_editable() && clear_button_enabled) {
365 bool press_attempt = clear_button_status.press_attempt;
366 clear_button_status.press_attempt = false;
367 if (press_attempt && clear_button_status.pressing_inside && _is_over_clear_button(b->get_position())) {
368 clear();
369 return;
370 }
371 }
372
373 if ((!selection.creating) && (!selection.double_click)) {
374 deselect();
375 }
376 selection.creating = false;
377 selection.double_click = false;
378 if (!drag_action) {
379 selection.drag_attempt = false;
380 }
381
382 if (pending_select_all_on_focus) {
383 select_all();
384 pending_select_all_on_focus = false;
385 }
386
387 show_virtual_keyboard();
388 }
389
390 queue_redraw();
391 return;
392 }
393
394 Ref<InputEventMouseMotion> m = p_event;
395
396 if (m.is_valid()) {
397 if (!text.is_empty() && is_editable() && clear_button_enabled) {
398 bool last_press_inside = clear_button_status.pressing_inside;
399 clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position());
400 if (last_press_inside != clear_button_status.pressing_inside) {
401 queue_redraw();
402 }
403 }
404
405 if (m->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
406 if (selection.creating) {
407 set_caret_at_pixel_pos(m->get_position().x);
408 selection_fill_at_caret();
409 }
410 }
411
412 if (drag_action && can_drop_data(m->get_position(), get_viewport()->gui_get_drag_data())) {
413 drag_caret_force_displayed = true;
414 set_caret_at_pixel_pos(m->get_position().x);
415 }
416
417 return;
418 }
419
420 Ref<InputEventKey> k = p_event;
421
422 if (k.is_valid()) {
423 if (!k->is_pressed()) {
424 if (alt_start && k->get_keycode() == Key::ALT) {
425 alt_start = false;
426 if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) {
427 char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
428 insert_text_at_caret(ucodestr);
429 }
430 accept_event();
431 return;
432 }
433 return;
434 }
435
436 // Alt + Unicode input:
437 if (k->is_alt_pressed()) {
438 if (!alt_start) {
439 if (k->get_keycode() == Key::KP_ADD) {
440 alt_start = true;
441 alt_code = 0;
442 accept_event();
443 return;
444 }
445 } else {
446 if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) {
447 alt_code = alt_code << 4;
448 alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0);
449 }
450 if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) {
451 alt_code = alt_code << 4;
452 alt_code += (uint32_t)(k->get_keycode() - Key::KP_0);
453 }
454 if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
455 alt_code = alt_code << 4;
456 alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10;
457 }
458 accept_event();
459 return;
460 }
461 }
462
463 if (context_menu_enabled) {
464 if (k->is_action("ui_menu", true)) {
465 _update_context_menu();
466 Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2);
467 menu->set_position(get_screen_position() + pos);
468 menu->reset_size();
469 menu->popup();
470 menu->grab_focus();
471
472 accept_event();
473 return;
474 }
475 }
476
477 // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE.
478 if (k->is_action("ui_text_submit", false)) {
479 emit_signal(SNAME("text_submitted"), text);
480 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
481 DisplayServer::get_singleton()->virtual_keyboard_hide();
482 }
483 accept_event();
484 return;
485 }
486
487 if (k->is_action("ui_cancel")) {
488 callable_mp((Control *)this, &Control::release_focus).call_deferred();
489 return;
490 }
491
492 if (is_shortcut_keys_enabled()) {
493 if (k->is_action("ui_copy", true)) {
494 copy_text();
495 accept_event();
496 return;
497 }
498
499 if (k->is_action("ui_text_select_all", true)) {
500 select();
501 accept_event();
502 return;
503 }
504
505 // Cut / Paste
506 if (k->is_action("ui_cut", true)) {
507 cut_text();
508 accept_event();
509 return;
510 }
511
512 if (k->is_action("ui_paste", true)) {
513 paste_text();
514 accept_event();
515 return;
516 }
517
518 // Undo / Redo
519 if (k->is_action("ui_undo", true)) {
520 undo();
521 accept_event();
522 return;
523 }
524
525 if (k->is_action("ui_redo", true)) {
526 redo();
527 accept_event();
528 return;
529 }
530 }
531
532 // BACKSPACE
533 if (k->is_action("ui_text_backspace_all_to_left", true)) {
534 _backspace(false, true);
535 accept_event();
536 return;
537 }
538 if (k->is_action("ui_text_backspace_word", true)) {
539 _backspace(true);
540 accept_event();
541 return;
542 }
543 if (k->is_action("ui_text_backspace", true)) {
544 _backspace();
545 accept_event();
546 return;
547 }
548
549 // DELETE
550 if (k->is_action("ui_text_delete_all_to_right", true)) {
551 _delete(false, true);
552 accept_event();
553 return;
554 }
555 if (k->is_action("ui_text_delete_word", true)) {
556 _delete(true);
557 accept_event();
558 return;
559 }
560 if (k->is_action("ui_text_delete", true)) {
561 _delete();
562 accept_event();
563 return;
564 }
565
566 // Cursor Movement
567
568 k = k->duplicate();
569 bool shift_pressed = k->is_shift_pressed();
570 // Remove shift or else actions will not match. Use above variable for selection.
571 k->set_shift_pressed(false);
572
573 if (k->is_action("ui_text_caret_word_left", true)) {
574 _move_caret_left(shift_pressed, true);
575 accept_event();
576 return;
577 }
578 if (k->is_action("ui_text_caret_left", true)) {
579 _move_caret_left(shift_pressed);
580 accept_event();
581 return;
582 }
583 if (k->is_action("ui_text_caret_word_right", true)) {
584 _move_caret_right(shift_pressed, true);
585 accept_event();
586 return;
587 }
588 if (k->is_action("ui_text_caret_right", true)) {
589 _move_caret_right(shift_pressed, false);
590 accept_event();
591 return;
592 }
593
594 // Up = Home, Down = End
595 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)) {
596 _move_caret_start(shift_pressed);
597 accept_event();
598 return;
599 }
600 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)) {
601 _move_caret_end(shift_pressed);
602 accept_event();
603 return;
604 }
605
606 // Misc
607 if (k->is_action("ui_swap_input_direction", true)) {
608 _swap_current_input_direction();
609 accept_event();
610 return;
611 }
612
613 _reset_caret_blink_timer();
614
615 // Allow unicode handling if:
616 // * No Modifiers are pressed (except shift)
617 bool allow_unicode_handling = !(k->is_command_or_control_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed());
618
619 if (allow_unicode_handling && editable && k->get_unicode() >= 32) {
620 // Handle Unicode if no modifiers are active.
621 selection_delete();
622 char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
623 int prev_len = text.length();
624 insert_text_at_caret(ucodestr);
625 if (text.length() != prev_len) {
626 _text_changed();
627 }
628 accept_event();
629 return;
630 }
631 }
632}
633
634void LineEdit::set_horizontal_alignment(HorizontalAlignment p_alignment) {
635 ERR_FAIL_INDEX((int)p_alignment, 4);
636 if (alignment == p_alignment) {
637 return;
638 }
639
640 alignment = p_alignment;
641 _shape();
642 queue_redraw();
643}
644
645HorizontalAlignment LineEdit::get_horizontal_alignment() const {
646 return alignment;
647}
648
649Variant LineEdit::get_drag_data(const Point2 &p_point) {
650 Variant ret = Control::get_drag_data(p_point);
651 if (ret != Variant()) {
652 return ret;
653 }
654
655 if (selection.drag_attempt && selection.enabled) {
656 String t = get_selected_text();
657 Label *l = memnew(Label);
658 l->set_text(t);
659 set_drag_preview(l);
660 return t;
661 }
662
663 return Variant();
664}
665
666bool LineEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
667 bool drop_override = Control::can_drop_data(p_point, p_data); // In case user wants to drop custom data.
668 if (drop_override) {
669 return drop_override;
670 }
671
672 return is_editable() && p_data.get_type() == Variant::STRING;
673}
674
675void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
676 Control::drop_data(p_point, p_data);
677
678 if (p_data.get_type() == Variant::STRING && is_editable()) {
679 set_caret_at_pixel_pos(p_point.x);
680 int caret_column_tmp = caret_column;
681 bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end;
682 if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
683 is_inside_sel = selection.enabled && caret_column > selection.begin && caret_column < selection.end;
684 }
685 if (selection.drag_attempt) {
686 selection.drag_attempt = false;
687 if (!is_inside_sel) {
688 if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) {
689 if (caret_column_tmp > selection.end) {
690 caret_column_tmp = caret_column_tmp - (selection.end - selection.begin);
691 }
692 selection_delete();
693 }
694
695 set_caret_column(caret_column_tmp);
696 insert_text_at_caret(p_data);
697 }
698 } else if (selection.enabled && caret_column >= selection.begin && caret_column <= selection.end) {
699 caret_column_tmp = selection.begin;
700 selection_delete();
701 set_caret_column(caret_column_tmp);
702 insert_text_at_caret(p_data);
703 grab_focus();
704 } else {
705 insert_text_at_caret(p_data);
706 grab_focus();
707 }
708 select(caret_column_tmp, caret_column);
709 if (!text_changed_dirty) {
710 if (is_inside_tree()) {
711 MessageQueue::get_singleton()->push_call(this, "_text_changed");
712 }
713 text_changed_dirty = true;
714 }
715 queue_redraw();
716 }
717}
718
719Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const {
720 if ((!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) || (!is_editable() && (!is_selecting_enabled() || text.is_empty()))) {
721 return CURSOR_ARROW;
722 }
723 return Control::get_cursor_shape(p_pos);
724}
725
726bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const {
727 if (!clear_button_enabled || !has_point(p_pos)) {
728 return false;
729 }
730 Ref<Texture2D> icon = theme_cache.clear_icon;
731 int x_ofs = theme_cache.normal->get_margin(SIDE_RIGHT);
732 return p_pos.x > get_size().width - icon->get_width() - x_ofs;
733}
734
735void LineEdit::_update_theme_item_cache() {
736 Control::_update_theme_item_cache();
737
738 theme_cache.base_scale = get_theme_default_base_scale();
739}
740
741void LineEdit::_notification(int p_what) {
742 switch (p_what) {
743#ifdef TOOLS_ENABLED
744 case NOTIFICATION_ENTER_TREE: {
745 if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
746 set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
747 set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval"));
748
749 if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) {
750 EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed));
751 }
752 }
753 } break;
754#endif
755
756 case NOTIFICATION_RESIZED: {
757 _fit_to_width();
758 scroll_offset = 0.0;
759 set_caret_column(get_caret_column());
760 } break;
761
762 case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
763 case NOTIFICATION_THEME_CHANGED: {
764 _shape();
765 queue_redraw();
766 } break;
767
768 case NOTIFICATION_TRANSLATION_CHANGED: {
769 placeholder_translated = atr(placeholder);
770 _shape();
771 queue_redraw();
772 } break;
773
774 case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
775 window_has_focus = true;
776 _validate_caret_can_draw();
777 queue_redraw();
778 } break;
779
780 case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
781 window_has_focus = false;
782 _validate_caret_can_draw();
783 queue_redraw();
784 } break;
785
786 case NOTIFICATION_INTERNAL_PROCESS: {
787 if (caret_blink_enabled && caret_can_draw) {
788 caret_blink_timer += get_process_delta_time();
789
790 if (caret_blink_timer >= caret_blink_interval) {
791 caret_blink_timer = 0.0;
792 _toggle_draw_caret();
793 }
794 }
795 } break;
796
797 case NOTIFICATION_DRAW: {
798 int width, height;
799 bool rtl = is_layout_rtl();
800
801 Size2 size = get_size();
802 width = size.width;
803 height = size.height;
804
805 RID ci = get_canvas_item();
806
807 Ref<StyleBox> style = theme_cache.normal;
808 if (!is_editable()) {
809 style = theme_cache.read_only;
810 }
811 Ref<Font> font = theme_cache.font;
812
813 if (!flat) {
814 style->draw(ci, Rect2(Point2(), size));
815 }
816
817 if (has_focus()) {
818 theme_cache.focus->draw(ci, Rect2(Point2(), size));
819 }
820
821 int x_ofs = 0;
822 bool using_placeholder = text.is_empty() && ime_text.is_empty();
823 float text_width = TS->shaped_text_get_size(text_rid).x;
824 float text_height = TS->shaped_text_get_size(text_rid).y;
825
826 switch (alignment) {
827 case HORIZONTAL_ALIGNMENT_FILL:
828 case HORIZONTAL_ALIGNMENT_LEFT: {
829 if (rtl) {
830 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - Math::ceil(style->get_margin(SIDE_RIGHT) + (text_width))));
831 } else {
832 x_ofs = style->get_offset().x;
833 }
834 } break;
835 case HORIZONTAL_ALIGNMENT_CENTER: {
836 if (!Math::is_zero_approx(scroll_offset)) {
837 x_ofs = style->get_offset().x;
838 } else {
839 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2);
840 }
841 } break;
842 case HORIZONTAL_ALIGNMENT_RIGHT: {
843 if (rtl) {
844 x_ofs = style->get_offset().x;
845 } else {
846 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - Math::ceil(style->get_margin(SIDE_RIGHT) + (text_width))));
847 }
848 } break;
849 }
850
851 int ofs_max = width - style->get_margin(SIDE_RIGHT);
852
853 int y_area = height - style->get_minimum_size().height;
854 int y_ofs = style->get_offset().y + (y_area - text_height) / 2;
855
856 Color selection_color = theme_cache.selection_color;
857 Color font_color;
858 if (is_editable()) {
859 font_color = theme_cache.font_color;
860 } else {
861 font_color = theme_cache.font_uneditable_color;
862 }
863 Color font_selected_color = theme_cache.font_selected_color;
864 Color caret_color = theme_cache.caret_color;
865
866 // Draw placeholder color.
867 if (using_placeholder) {
868 font_color = theme_cache.font_placeholder_color;
869 }
870
871 bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
872 if (right_icon.is_valid() || display_clear_icon) {
873 Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
874 Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9);
875 if (display_clear_icon) {
876 if (clear_button_status.press_attempt && clear_button_status.pressing_inside) {
877 color_icon = theme_cache.clear_button_color_pressed;
878 } else {
879 color_icon = theme_cache.clear_button_color;
880 }
881 }
882
883 r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon);
884
885 if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
886 if (Math::is_zero_approx(scroll_offset)) {
887 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
888 }
889 } else {
890 x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT));
891 }
892
893 ofs_max -= r_icon->get_width();
894 }
895
896 // Draw selections rects.
897 Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs);
898 if (selection.enabled) {
899 Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, selection.begin, selection.end);
900 for (int i = 0; i < sel.size(); i++) {
901 Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
902 if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
903 continue;
904 }
905 if (rect.position.x < x_ofs) {
906 rect.size.x -= (x_ofs - rect.position.x);
907 rect.position.x = x_ofs;
908 } else if (rect.position.x + rect.size.x > ofs_max) {
909 rect.size.x = ofs_max - rect.position.x;
910 }
911 RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color);
912 }
913 }
914 const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid);
915 int gl_size = TS->shaped_text_get_glyph_count(text_rid);
916
917 // Draw text.
918 ofs.y += TS->shaped_text_get_ascent(text_rid);
919 Color font_outline_color = theme_cache.font_outline_color;
920 int outline_size = theme_cache.font_outline_size;
921 if (outline_size > 0 && font_outline_color.a > 0) {
922 Vector2 oofs = ofs;
923 for (int i = 0; i < gl_size; i++) {
924 for (int j = 0; j < glyphs[i].repeat; j++) {
925 if (ceil(oofs.x) >= x_ofs && (oofs.x + glyphs[i].advance) <= ofs_max) {
926 if (glyphs[i].font_rid != RID()) {
927 TS->font_draw_glyph_outline(glyphs[i].font_rid, ci, glyphs[i].font_size, outline_size, oofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, font_outline_color);
928 }
929 }
930 oofs.x += glyphs[i].advance;
931 }
932 if (oofs.x >= ofs_max) {
933 break;
934 }
935 }
936 }
937 for (int i = 0; i < gl_size; i++) {
938 bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end;
939 for (int j = 0; j < glyphs[i].repeat; j++) {
940 if (ceil(ofs.x) >= x_ofs && (ofs.x + glyphs[i].advance) <= ofs_max) {
941 if (glyphs[i].font_rid != RID()) {
942 TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color);
943 } else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
944 TS->draw_hex_code_box(ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color);
945 }
946 }
947 ofs.x += glyphs[i].advance;
948 }
949 if (ofs.x >= ofs_max) {
950 break;
951 }
952 }
953
954 // Draw carets.
955 ofs.x = x_ofs + scroll_offset;
956 if ((caret_can_draw && draw_caret) || drag_caret_force_displayed) {
957 // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
958 const int caret_width = theme_cache.caret_width * MAX(1, theme_cache.base_scale);
959
960 if (ime_text.is_empty() || ime_selection.y == 0) {
961 // Normal caret.
962 CaretInfo caret = TS->shaped_text_get_carets(text_rid, ime_text.is_empty() ? caret_column : caret_column + ime_selection.x);
963 if (using_placeholder || (caret.l_caret == Rect2() && caret.t_caret == Rect2())) {
964 // No carets, add one at the start.
965 int h = theme_cache.font->get_height(theme_cache.font_size);
966 int y = style->get_offset().y + (y_area - h) / 2;
967 caret.l_dir = (rtl) ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR;
968 switch (alignment) {
969 case HORIZONTAL_ALIGNMENT_FILL:
970 case HORIZONTAL_ALIGNMENT_LEFT: {
971 if (rtl) {
972 caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
973 } else {
974 caret.l_caret = Rect2(Vector2(style->get_offset().x, y), Size2(caret_width, h));
975 }
976 } break;
977 case HORIZONTAL_ALIGNMENT_CENTER: {
978 caret.l_caret = Rect2(Vector2(size.x / 2, y), Size2(caret_width, h));
979 } break;
980 case HORIZONTAL_ALIGNMENT_RIGHT: {
981 if (rtl) {
982 caret.l_caret = Rect2(Vector2(style->get_offset().x, y), Size2(caret_width, h));
983 } else {
984 caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
985 }
986 } break;
987 }
988
989 RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color);
990 } else {
991 if (caret.l_caret != Rect2() && caret.l_dir == TextServer::DIRECTION_AUTO) {
992 // Draw extra marker on top of mid caret.
993 Rect2 trect = Rect2(caret.l_caret.position.x - 2.5 * caret_width, caret.l_caret.position.y, 6 * caret_width, caret_width);
994 trect.position += ofs;
995 RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
996 } else if (caret.l_caret != Rect2() && caret.t_caret != Rect2() && caret.l_dir != caret.t_dir) {
997 // Draw extra direction marker on top of split caret.
998 float d = (caret.l_dir == TextServer::DIRECTION_LTR) ? 0.5 : -3;
999 Rect2 trect = Rect2(caret.l_caret.position.x + d * caret_width, caret.l_caret.position.y + caret.l_caret.size.y - caret_width, 3 * caret_width, caret_width);
1000 trect.position += ofs;
1001 RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
1002
1003 d = (caret.t_dir == TextServer::DIRECTION_LTR) ? 0.5 : -3;
1004 trect = Rect2(caret.t_caret.position.x + d * caret_width, caret.t_caret.position.y, 3 * caret_width, caret_width);
1005 trect.position += ofs;
1006 RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
1007 }
1008
1009 caret.l_caret.position += ofs;
1010 caret.l_caret.size.x = caret_width;
1011 RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color);
1012
1013 caret.t_caret.position += ofs;
1014 caret.t_caret.size.x = caret_width;
1015
1016 RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.t_caret, caret_color);
1017 }
1018 }
1019 if (!ime_text.is_empty()) {
1020 {
1021 // IME intermediate text range.
1022 Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length());
1023 for (int i = 0; i < sel.size(); i++) {
1024 Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
1025 if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
1026 continue;
1027 }
1028 if (rect.position.x < x_ofs) {
1029 rect.size.x -= (x_ofs - rect.position.x);
1030 rect.position.x = x_ofs;
1031 } else if (rect.position.x + rect.size.x > ofs_max) {
1032 rect.size.x = ofs_max - rect.position.x;
1033 }
1034 rect.size.y = caret_width;
1035 RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color);
1036 }
1037 }
1038 {
1039 // IME caret.
1040 if (ime_selection.y > 0) {
1041 Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y);
1042 for (int i = 0; i < sel.size(); i++) {
1043 Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
1044 if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
1045 continue;
1046 }
1047 if (rect.position.x < x_ofs) {
1048 rect.size.x -= (x_ofs - rect.position.x);
1049 rect.position.x = x_ofs;
1050 } else if (rect.position.x + rect.size.x > ofs_max) {
1051 rect.size.x = ofs_max - rect.position.x;
1052 }
1053 rect.size.y = caret_width * 3;
1054 RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color);
1055 }
1056 }
1057 }
1058 }
1059 }
1060
1061 if (has_focus()) {
1062 if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
1063 DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
1064 Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position();
1065 if (get_window()->get_embedder()) {
1066 pos += get_viewport()->get_popup_base_transform().get_origin();
1067 }
1068 DisplayServer::get_singleton()->window_set_ime_position(pos, get_viewport()->get_window_id());
1069 }
1070 }
1071 } break;
1072
1073 case NOTIFICATION_FOCUS_ENTER: {
1074 _validate_caret_can_draw();
1075
1076 if (select_all_on_focus) {
1077 if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
1078 // Select all when the mouse button is up.
1079 pending_select_all_on_focus = true;
1080 } else {
1081 select_all();
1082 }
1083 }
1084
1085 if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
1086 DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
1087 Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position();
1088 if (get_window()->get_embedder()) {
1089 pos += get_viewport()->get_popup_base_transform().get_origin();
1090 }
1091 DisplayServer::get_singleton()->window_set_ime_position(pos, get_viewport()->get_window_id());
1092 }
1093
1094 show_virtual_keyboard();
1095 } break;
1096
1097 case NOTIFICATION_FOCUS_EXIT: {
1098 _validate_caret_can_draw();
1099
1100 if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
1101 DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
1102 DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
1103 }
1104 ime_text = "";
1105 ime_selection = Point2();
1106 _shape();
1107 set_caret_column(caret_column); // Update scroll_offset.
1108
1109 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
1110 DisplayServer::get_singleton()->virtual_keyboard_hide();
1111 }
1112
1113 if (deselect_on_focus_loss_enabled && !selection.drag_attempt) {
1114 deselect();
1115 }
1116 } break;
1117
1118 case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
1119 if (has_focus()) {
1120 ime_text = DisplayServer::get_singleton()->ime_get_text();
1121 ime_selection = DisplayServer::get_singleton()->ime_get_selection();
1122
1123 if (!ime_text.is_empty()) {
1124 selection_delete();
1125 }
1126
1127 _shape();
1128 set_caret_column(caret_column); // Update scroll_offset.
1129
1130 queue_redraw();
1131 }
1132 } break;
1133
1134 case NOTIFICATION_DRAG_BEGIN: {
1135 drag_action = true;
1136 } break;
1137
1138 case NOTIFICATION_DRAG_END: {
1139 if (is_drag_successful()) {
1140 if (selection.drag_attempt) {
1141 selection.drag_attempt = false;
1142 if (is_editable() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
1143 selection_delete();
1144 } else if (deselect_on_focus_loss_enabled) {
1145 deselect();
1146 }
1147 }
1148 } else {
1149 selection.drag_attempt = false;
1150 }
1151 drag_action = false;
1152 drag_caret_force_displayed = false;
1153 } break;
1154 }
1155}
1156
1157void LineEdit::copy_text() {
1158 if (selection.enabled && !pass) {
1159 DisplayServer::get_singleton()->clipboard_set(get_selected_text());
1160 }
1161}
1162
1163void LineEdit::cut_text() {
1164 if (editable && selection.enabled && !pass) {
1165 DisplayServer::get_singleton()->clipboard_set(get_selected_text());
1166 selection_delete();
1167 }
1168}
1169
1170void LineEdit::paste_text() {
1171 if (!editable) {
1172 return;
1173 }
1174
1175 // Strip escape characters like \n and \t as they can't be displayed on LineEdit.
1176 String paste_buffer = DisplayServer::get_singleton()->clipboard_get().strip_escapes();
1177
1178 if (!paste_buffer.is_empty()) {
1179 int prev_len = text.length();
1180 if (selection.enabled) {
1181 selection_delete();
1182 }
1183 insert_text_at_caret(paste_buffer);
1184
1185 if (!text_changed_dirty) {
1186 if (is_inside_tree() && text.length() != prev_len) {
1187 MessageQueue::get_singleton()->push_call(this, "_text_changed");
1188 }
1189 text_changed_dirty = true;
1190 }
1191 }
1192}
1193
1194bool LineEdit::has_undo() const {
1195 if (undo_stack_pos == nullptr) {
1196 return undo_stack.size() > 1;
1197 }
1198 return undo_stack_pos != undo_stack.front();
1199}
1200
1201bool LineEdit::has_redo() const {
1202 return undo_stack_pos != nullptr && undo_stack_pos != undo_stack.back();
1203}
1204
1205void LineEdit::undo() {
1206 if (!editable) {
1207 return;
1208 }
1209
1210 if (undo_stack_pos == nullptr) {
1211 if (undo_stack.size() <= 1) {
1212 return;
1213 }
1214 undo_stack_pos = undo_stack.back();
1215 } else if (undo_stack_pos == undo_stack.front()) {
1216 return;
1217 }
1218
1219 deselect();
1220
1221 undo_stack_pos = undo_stack_pos->prev();
1222 TextOperation op = undo_stack_pos->get();
1223 text = op.text;
1224 scroll_offset = op.scroll_offset;
1225 set_caret_column(op.caret_column);
1226
1227 _shape();
1228 _emit_text_change();
1229}
1230
1231void LineEdit::redo() {
1232 if (!editable) {
1233 return;
1234 }
1235
1236 if (undo_stack_pos == nullptr) {
1237 return;
1238 }
1239 if (undo_stack_pos == undo_stack.back()) {
1240 return;
1241 }
1242
1243 deselect();
1244
1245 undo_stack_pos = undo_stack_pos->next();
1246 TextOperation op = undo_stack_pos->get();
1247 text = op.text;
1248 scroll_offset = op.scroll_offset;
1249 set_caret_column(op.caret_column);
1250
1251 _shape();
1252 _emit_text_change();
1253}
1254
1255void LineEdit::shift_selection_check_pre(bool p_shift) {
1256 if (!selection.enabled && p_shift) {
1257 selection.start_column = caret_column;
1258 }
1259 if (!p_shift) {
1260 deselect();
1261 }
1262}
1263
1264void LineEdit::shift_selection_check_post(bool p_shift) {
1265 if (p_shift) {
1266 selection_fill_at_caret();
1267 }
1268}
1269
1270void LineEdit::set_caret_at_pixel_pos(int p_x) {
1271 Ref<StyleBox> style = theme_cache.normal;
1272 bool rtl = is_layout_rtl();
1273
1274 int x_ofs = 0;
1275 float text_width = TS->shaped_text_get_size(text_rid).x;
1276 switch (alignment) {
1277 case HORIZONTAL_ALIGNMENT_FILL:
1278 case HORIZONTAL_ALIGNMENT_LEFT: {
1279 if (rtl) {
1280 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
1281 } else {
1282 x_ofs = style->get_offset().x;
1283 }
1284 } break;
1285 case HORIZONTAL_ALIGNMENT_CENTER: {
1286 if (!Math::is_zero_approx(scroll_offset)) {
1287 x_ofs = style->get_offset().x;
1288 } else {
1289 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
1290 }
1291 } break;
1292 case HORIZONTAL_ALIGNMENT_RIGHT: {
1293 if (rtl) {
1294 x_ofs = style->get_offset().x;
1295 } else {
1296 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
1297 }
1298 } break;
1299 }
1300
1301 bool using_placeholder = text.is_empty() && ime_text.is_empty();
1302 bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
1303 if (right_icon.is_valid() || display_clear_icon) {
1304 Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
1305 if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
1306 if (Math::is_zero_approx(scroll_offset)) {
1307 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);
1308 }
1309 } else {
1310 x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT));
1311 }
1312 }
1313
1314 int ofs = ceil(TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset));
1315 if (!caret_mid_grapheme_enabled) {
1316 ofs = TS->shaped_text_closest_character_pos(text_rid, ofs);
1317 }
1318 set_caret_column(ofs);
1319}
1320
1321Vector2 LineEdit::get_caret_pixel_pos() {
1322 Ref<StyleBox> style = theme_cache.normal;
1323 bool rtl = is_layout_rtl();
1324
1325 int x_ofs = 0;
1326 float text_width = TS->shaped_text_get_size(text_rid).x;
1327 switch (alignment) {
1328 case HORIZONTAL_ALIGNMENT_FILL:
1329 case HORIZONTAL_ALIGNMENT_LEFT: {
1330 if (rtl) {
1331 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
1332 } else {
1333 x_ofs = style->get_offset().x;
1334 }
1335 } break;
1336 case HORIZONTAL_ALIGNMENT_CENTER: {
1337 if (!Math::is_zero_approx(scroll_offset)) {
1338 x_ofs = style->get_offset().x;
1339 } else {
1340 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
1341 }
1342 } break;
1343 case HORIZONTAL_ALIGNMENT_RIGHT: {
1344 if (rtl) {
1345 x_ofs = style->get_offset().x;
1346 } else {
1347 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
1348 }
1349 } break;
1350 }
1351
1352 bool using_placeholder = text.is_empty() && ime_text.is_empty();
1353 bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
1354 if (right_icon.is_valid() || display_clear_icon) {
1355 Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
1356 if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
1357 if (Math::is_zero_approx(scroll_offset)) {
1358 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);
1359 }
1360 } else {
1361 x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT));
1362 }
1363 }
1364
1365 Vector2 ret;
1366 CaretInfo caret;
1367 // Get position of the start of caret.
1368 if (ime_text.length() != 0 && ime_selection.x != 0) {
1369 caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x);
1370 } else {
1371 caret = TS->shaped_text_get_carets(text_rid, caret_column);
1372 }
1373
1374 if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) {
1375 ret.x = x_ofs + caret.l_caret.position.x + scroll_offset;
1376 } else {
1377 ret.x = x_ofs + caret.t_caret.position.x + scroll_offset;
1378 }
1379
1380 // Get position of the end of caret.
1381 if (ime_text.length() != 0) {
1382 if (ime_selection.y != 0) {
1383 caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y);
1384 } else {
1385 caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size());
1386 }
1387 if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) {
1388 ret.y = x_ofs + caret.l_caret.position.x + scroll_offset;
1389 } else {
1390 ret.y = x_ofs + caret.t_caret.position.x + scroll_offset;
1391 }
1392 } else {
1393 ret.y = ret.x;
1394 }
1395
1396 return ret;
1397}
1398
1399void LineEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) {
1400 caret_mid_grapheme_enabled = p_enabled;
1401}
1402
1403bool LineEdit::is_caret_mid_grapheme_enabled() const {
1404 return caret_mid_grapheme_enabled;
1405}
1406
1407bool LineEdit::is_caret_blink_enabled() const {
1408 return caret_blink_enabled;
1409}
1410
1411void LineEdit::set_caret_blink_enabled(const bool p_enabled) {
1412 if (caret_blink_enabled == p_enabled) {
1413 return;
1414 }
1415
1416 caret_blink_enabled = p_enabled;
1417 set_process_internal(p_enabled);
1418
1419 draw_caret = !caret_blink_enabled;
1420 if (caret_blink_enabled) {
1421 caret_blink_timer = 0.0;
1422 }
1423 queue_redraw();
1424
1425 notify_property_list_changed();
1426}
1427
1428bool LineEdit::is_caret_force_displayed() const {
1429 return caret_force_displayed;
1430}
1431
1432void LineEdit::set_caret_force_displayed(const bool p_enabled) {
1433 if (caret_force_displayed == p_enabled) {
1434 return;
1435 }
1436
1437 caret_force_displayed = p_enabled;
1438 _validate_caret_can_draw();
1439
1440 queue_redraw();
1441}
1442
1443float LineEdit::get_caret_blink_interval() const {
1444 return caret_blink_interval;
1445}
1446
1447void LineEdit::set_caret_blink_interval(const float p_interval) {
1448 ERR_FAIL_COND(p_interval <= 0);
1449 caret_blink_interval = p_interval;
1450}
1451
1452void LineEdit::_reset_caret_blink_timer() {
1453 if (caret_blink_enabled) {
1454 draw_caret = true;
1455 if (caret_can_draw) {
1456 caret_blink_timer = 0.0;
1457 queue_redraw();
1458 }
1459 }
1460}
1461
1462void LineEdit::_toggle_draw_caret() {
1463 draw_caret = !draw_caret;
1464 if (is_visible_in_tree() && caret_can_draw) {
1465 queue_redraw();
1466 }
1467}
1468
1469void LineEdit::_validate_caret_can_draw() {
1470 if (caret_blink_enabled) {
1471 draw_caret = true;
1472 caret_blink_timer = 0.0;
1473 }
1474 caret_can_draw = editable && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed);
1475}
1476
1477void LineEdit::delete_char() {
1478 if ((text.length() <= 0) || (caret_column == 0)) {
1479 return;
1480 }
1481
1482 text = text.left(caret_column - 1) + text.substr(caret_column);
1483 _shape();
1484
1485 set_caret_column(get_caret_column() - 1);
1486
1487 _text_changed();
1488}
1489
1490void LineEdit::delete_text(int p_from_column, int p_to_column) {
1491 ERR_FAIL_COND_MSG(p_from_column < 0 || p_from_column > p_to_column || p_to_column > text.length(),
1492 vformat("Positional parameters (from: %d, to: %d) are inverted or outside the text length (%d).", p_from_column, p_to_column, text.length()));
1493
1494 text = text.left(p_from_column) + text.substr(p_to_column);
1495 _shape();
1496
1497 set_caret_column(caret_column - CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column));
1498
1499 if (!text_changed_dirty) {
1500 if (is_inside_tree()) {
1501 MessageQueue::get_singleton()->push_call(this, "_text_changed");
1502 }
1503 text_changed_dirty = true;
1504 }
1505}
1506
1507void LineEdit::set_text(String p_text) {
1508 clear_internal();
1509 insert_text_at_caret(p_text);
1510 _create_undo_state();
1511
1512 queue_redraw();
1513 caret_column = 0;
1514 scroll_offset = 0.0;
1515}
1516
1517void LineEdit::set_text_with_selection(const String &p_text) {
1518 Selection selection_copy = selection;
1519
1520 clear_internal();
1521 insert_text_at_caret(p_text);
1522 _create_undo_state();
1523
1524 int tlen = text.length();
1525 selection = selection_copy;
1526 selection.begin = MIN(selection.begin, tlen);
1527 selection.end = MIN(selection.end, tlen);
1528 selection.start_column = MIN(selection.start_column, tlen);
1529
1530 queue_redraw();
1531}
1532
1533void LineEdit::set_text_direction(Control::TextDirection p_text_direction) {
1534 ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
1535 if (text_direction != p_text_direction) {
1536 text_direction = p_text_direction;
1537 if (text_direction != TEXT_DIRECTION_AUTO && text_direction != TEXT_DIRECTION_INHERITED) {
1538 input_direction = text_direction;
1539 }
1540 _shape();
1541
1542 if (menu_dir) {
1543 menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED);
1544 menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO);
1545 menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR);
1546 menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL);
1547 }
1548 queue_redraw();
1549 }
1550}
1551
1552Control::TextDirection LineEdit::get_text_direction() const {
1553 return text_direction;
1554}
1555
1556void LineEdit::set_language(const String &p_language) {
1557 if (language != p_language) {
1558 language = p_language;
1559 _shape();
1560 queue_redraw();
1561 }
1562}
1563
1564String LineEdit::get_language() const {
1565 return language;
1566}
1567
1568void LineEdit::set_draw_control_chars(bool p_draw_control_chars) {
1569 if (draw_control_chars != p_draw_control_chars) {
1570 draw_control_chars = p_draw_control_chars;
1571 if (menu && menu->get_item_index(MENU_DISPLAY_UCC) >= 0) {
1572 menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
1573 }
1574 _shape();
1575 queue_redraw();
1576 }
1577}
1578
1579bool LineEdit::get_draw_control_chars() const {
1580 return draw_control_chars;
1581}
1582
1583void LineEdit::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
1584 if (st_parser != p_parser) {
1585 st_parser = p_parser;
1586 _shape();
1587 queue_redraw();
1588 }
1589}
1590
1591TextServer::StructuredTextParser LineEdit::get_structured_text_bidi_override() const {
1592 return st_parser;
1593}
1594
1595void LineEdit::set_structured_text_bidi_override_options(Array p_args) {
1596 st_args = p_args;
1597 _shape();
1598 queue_redraw();
1599}
1600
1601Array LineEdit::get_structured_text_bidi_override_options() const {
1602 return st_args;
1603}
1604
1605void LineEdit::clear() {
1606 clear_internal();
1607 _text_changed();
1608
1609 // This should reset virtual keyboard state if needed.
1610 if (has_focus()) {
1611 show_virtual_keyboard();
1612 }
1613}
1614
1615void LineEdit::show_virtual_keyboard() {
1616 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
1617 if (selection.enabled) {
1618 DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), DisplayServer::VirtualKeyboardType(virtual_keyboard_type), max_length, selection.begin, selection.end);
1619 } else {
1620 DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), DisplayServer::VirtualKeyboardType(virtual_keyboard_type), max_length, caret_column);
1621 }
1622 }
1623}
1624
1625String LineEdit::get_text() const {
1626 return text;
1627}
1628
1629void LineEdit::set_placeholder(String p_text) {
1630 if (placeholder == p_text) {
1631 return;
1632 }
1633
1634 placeholder = p_text;
1635 placeholder_translated = atr(placeholder);
1636 _shape();
1637 queue_redraw();
1638}
1639
1640String LineEdit::get_placeholder() const {
1641 return placeholder;
1642}
1643
1644void LineEdit::set_caret_column(int p_column) {
1645 if (p_column > (int)text.length()) {
1646 p_column = text.length();
1647 }
1648
1649 if (p_column < 0) {
1650 p_column = 0;
1651 }
1652
1653 caret_column = p_column;
1654
1655 // Fit to window.
1656
1657 if (!is_inside_tree()) {
1658 scroll_offset = 0.0;
1659 return;
1660 }
1661
1662 Ref<StyleBox> style = theme_cache.normal;
1663 bool rtl = is_layout_rtl();
1664
1665 int x_ofs = 0;
1666 float text_width = TS->shaped_text_get_size(text_rid).x;
1667 switch (alignment) {
1668 case HORIZONTAL_ALIGNMENT_FILL:
1669 case HORIZONTAL_ALIGNMENT_LEFT: {
1670 if (rtl) {
1671 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
1672 } else {
1673 x_ofs = style->get_offset().x;
1674 }
1675 } break;
1676 case HORIZONTAL_ALIGNMENT_CENTER: {
1677 if (!Math::is_zero_approx(scroll_offset)) {
1678 x_ofs = style->get_offset().x;
1679 } else {
1680 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
1681 }
1682 } break;
1683 case HORIZONTAL_ALIGNMENT_RIGHT: {
1684 if (rtl) {
1685 x_ofs = style->get_offset().x;
1686 } else {
1687 x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
1688 }
1689 } break;
1690 }
1691
1692 int ofs_max = get_size().width - style->get_margin(SIDE_RIGHT);
1693 bool using_placeholder = text.is_empty() && ime_text.is_empty();
1694 bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
1695 if (right_icon.is_valid() || display_clear_icon) {
1696 Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
1697 if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
1698 if (Math::is_zero_approx(scroll_offset)) {
1699 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);
1700 }
1701 } else {
1702 x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT));
1703 }
1704 ofs_max -= r_icon->get_width();
1705 }
1706
1707 // Note: Use two coordinates to fit IME input range.
1708 Vector2 primary_caret_offset = get_caret_pixel_pos();
1709
1710 if (MIN(primary_caret_offset.x, primary_caret_offset.y) <= x_ofs) {
1711 scroll_offset += x_ofs - MIN(primary_caret_offset.x, primary_caret_offset.y);
1712 } else if (MAX(primary_caret_offset.x, primary_caret_offset.y) >= ofs_max) {
1713 scroll_offset += ofs_max - MAX(primary_caret_offset.x, primary_caret_offset.y);
1714 }
1715 scroll_offset = MIN(0, scroll_offset);
1716
1717 queue_redraw();
1718}
1719
1720int LineEdit::get_caret_column() const {
1721 return caret_column;
1722}
1723
1724void LineEdit::set_scroll_offset(float p_pos) {
1725 scroll_offset = p_pos;
1726 if (scroll_offset < 0.0) {
1727 scroll_offset = 0.0;
1728 }
1729}
1730
1731float LineEdit::get_scroll_offset() const {
1732 return scroll_offset;
1733}
1734
1735void LineEdit::insert_text_at_caret(String p_text) {
1736 if (max_length > 0) {
1737 // Truncate text to append to fit in max_length, if needed.
1738 int available_chars = max_length - text.length();
1739 if (p_text.length() > available_chars) {
1740 emit_signal(SNAME("text_change_rejected"), p_text.substr(available_chars));
1741 p_text = p_text.substr(0, available_chars);
1742 }
1743 }
1744 String pre = text.substr(0, caret_column);
1745 String post = text.substr(caret_column, text.length() - caret_column);
1746 text = pre + p_text + post;
1747 _shape();
1748 TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text_rid, caret_column, caret_column + p_text.length());
1749 if (dir != TextServer::DIRECTION_AUTO) {
1750 input_direction = (TextDirection)dir;
1751 }
1752 set_caret_column(caret_column + p_text.length());
1753
1754 if (!ime_text.is_empty()) {
1755 _shape();
1756 }
1757}
1758
1759void LineEdit::clear_internal() {
1760 deselect();
1761 _clear_undo_stack();
1762 caret_column = 0;
1763 scroll_offset = 0.0;
1764 undo_text = "";
1765 text = "";
1766 _shape();
1767 queue_redraw();
1768}
1769
1770Size2 LineEdit::get_minimum_size() const {
1771 Ref<StyleBox> style = theme_cache.normal;
1772 Ref<Font> font = theme_cache.font;
1773 int font_size = theme_cache.font_size;
1774
1775 Size2 min_size;
1776
1777 // Minimum size of text.
1778 float em_space_size = font->get_char_size('M', font_size).x;
1779 min_size.width = theme_cache.minimum_character_width * em_space_size;
1780
1781 if (expand_to_text_length) {
1782 // Ensure some space for the caret when placed at the end.
1783 min_size.width = MAX(min_size.width, full_width + theme_cache.caret_width);
1784 }
1785
1786 min_size.height = MAX(TS->shaped_text_get_size(text_rid).y, font->get_height(font_size));
1787
1788 // Take icons into account.
1789 int icon_max_width = 0;
1790 if (right_icon.is_valid()) {
1791 min_size.height = MAX(min_size.height, right_icon->get_height());
1792 icon_max_width = right_icon->get_width();
1793 }
1794 if (clear_button_enabled) {
1795 min_size.height = MAX(min_size.height, theme_cache.clear_icon->get_height());
1796 icon_max_width = MAX(icon_max_width, theme_cache.clear_icon->get_width());
1797 }
1798 min_size.width += icon_max_width;
1799
1800 return style->get_minimum_size() + min_size;
1801}
1802
1803void LineEdit::deselect() {
1804 selection.begin = 0;
1805 selection.end = 0;
1806 selection.start_column = 0;
1807 selection.enabled = false;
1808 selection.creating = false;
1809 selection.double_click = false;
1810 queue_redraw();
1811}
1812
1813bool LineEdit::has_selection() const {
1814 return selection.enabled;
1815}
1816
1817String LineEdit::get_selected_text() {
1818 if (selection.enabled) {
1819 return text.substr(selection.begin, selection.end - selection.begin);
1820 } else {
1821 return String();
1822 }
1823}
1824
1825int LineEdit::get_selection_from_column() const {
1826 ERR_FAIL_COND_V(!selection.enabled, -1);
1827 return selection.begin;
1828}
1829
1830int LineEdit::get_selection_to_column() const {
1831 ERR_FAIL_COND_V(!selection.enabled, -1);
1832 return selection.end;
1833}
1834
1835void LineEdit::selection_delete() {
1836 if (selection.enabled) {
1837 delete_text(selection.begin, selection.end);
1838 }
1839
1840 deselect();
1841}
1842
1843void LineEdit::set_max_length(int p_max_length) {
1844 ERR_FAIL_COND(p_max_length < 0);
1845 max_length = p_max_length;
1846 set_text(text);
1847}
1848
1849int LineEdit::get_max_length() const {
1850 return max_length;
1851}
1852
1853void LineEdit::selection_fill_at_caret() {
1854 if (!selecting_enabled) {
1855 return;
1856 }
1857
1858 selection.begin = caret_column;
1859 selection.end = selection.start_column;
1860
1861 if (selection.end < selection.begin) {
1862 int aux = selection.end;
1863 selection.end = selection.begin;
1864 selection.begin = aux;
1865 }
1866
1867 selection.enabled = (selection.begin != selection.end);
1868}
1869
1870void LineEdit::select_all() {
1871 if (!selecting_enabled) {
1872 return;
1873 }
1874
1875 if (!text.length()) {
1876 return;
1877 }
1878
1879 selection.begin = 0;
1880 selection.end = text.length();
1881 selection.enabled = true;
1882 queue_redraw();
1883}
1884
1885void LineEdit::set_editable(bool p_editable) {
1886 if (editable == p_editable) {
1887 return;
1888 }
1889
1890 editable = p_editable;
1891 _validate_caret_can_draw();
1892
1893 update_minimum_size();
1894 queue_redraw();
1895}
1896
1897bool LineEdit::is_editable() const {
1898 return editable;
1899}
1900
1901void LineEdit::set_secret(bool p_secret) {
1902 if (pass == p_secret) {
1903 return;
1904 }
1905
1906 pass = p_secret;
1907 _shape();
1908 queue_redraw();
1909}
1910
1911bool LineEdit::is_secret() const {
1912 return pass;
1913}
1914
1915void LineEdit::set_secret_character(const String &p_string) {
1916 // An empty string as the secret character would crash the engine.
1917 // It also wouldn't make sense to use multiple characters as the secret character.
1918 ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given).");
1919
1920 if (secret_character == p_string) {
1921 return;
1922 }
1923
1924 secret_character = p_string;
1925 _shape();
1926 queue_redraw();
1927}
1928
1929String LineEdit::get_secret_character() const {
1930 return secret_character;
1931}
1932
1933void LineEdit::select(int p_from, int p_to) {
1934 if (!selecting_enabled) {
1935 return;
1936 }
1937
1938 if (p_from == 0 && p_to == 0) {
1939 deselect();
1940 return;
1941 }
1942
1943 int len = text.length();
1944 if (p_from < 0) {
1945 p_from = 0;
1946 }
1947 if (p_from > len) {
1948 p_from = len;
1949 }
1950 if (p_to < 0 || p_to > len) {
1951 p_to = len;
1952 }
1953
1954 if (p_from >= p_to) {
1955 return;
1956 }
1957
1958 selection.enabled = true;
1959 selection.begin = p_from;
1960 selection.end = p_to;
1961 selection.creating = false;
1962 selection.double_click = false;
1963 queue_redraw();
1964}
1965
1966bool LineEdit::is_text_field() const {
1967 return true;
1968}
1969
1970void LineEdit::menu_option(int p_option) {
1971 switch (p_option) {
1972 case MENU_CUT: {
1973 if (editable) {
1974 cut_text();
1975 }
1976 } break;
1977 case MENU_COPY: {
1978 copy_text();
1979 } break;
1980 case MENU_PASTE: {
1981 if (editable) {
1982 paste_text();
1983 }
1984 } break;
1985 case MENU_CLEAR: {
1986 if (editable) {
1987 clear();
1988 }
1989 } break;
1990 case MENU_SELECT_ALL: {
1991 select_all();
1992 } break;
1993 case MENU_UNDO: {
1994 if (editable) {
1995 undo();
1996 }
1997 } break;
1998 case MENU_REDO: {
1999 if (editable) {
2000 redo();
2001 }
2002 } break;
2003 case MENU_DIR_INHERITED: {
2004 set_text_direction(TEXT_DIRECTION_INHERITED);
2005 } break;
2006 case MENU_DIR_AUTO: {
2007 set_text_direction(TEXT_DIRECTION_AUTO);
2008 } break;
2009 case MENU_DIR_LTR: {
2010 set_text_direction(TEXT_DIRECTION_LTR);
2011 } break;
2012 case MENU_DIR_RTL: {
2013 set_text_direction(TEXT_DIRECTION_RTL);
2014 } break;
2015 case MENU_DISPLAY_UCC: {
2016 set_draw_control_chars(!get_draw_control_chars());
2017 } break;
2018 case MENU_INSERT_LRM: {
2019 if (editable) {
2020 insert_text_at_caret(String::chr(0x200E));
2021 }
2022 } break;
2023 case MENU_INSERT_RLM: {
2024 if (editable) {
2025 insert_text_at_caret(String::chr(0x200F));
2026 }
2027 } break;
2028 case MENU_INSERT_LRE: {
2029 if (editable) {
2030 insert_text_at_caret(String::chr(0x202A));
2031 }
2032 } break;
2033 case MENU_INSERT_RLE: {
2034 if (editable) {
2035 insert_text_at_caret(String::chr(0x202B));
2036 }
2037 } break;
2038 case MENU_INSERT_LRO: {
2039 if (editable) {
2040 insert_text_at_caret(String::chr(0x202D));
2041 }
2042 } break;
2043 case MENU_INSERT_RLO: {
2044 if (editable) {
2045 insert_text_at_caret(String::chr(0x202E));
2046 }
2047 } break;
2048 case MENU_INSERT_PDF: {
2049 if (editable) {
2050 insert_text_at_caret(String::chr(0x202C));
2051 }
2052 } break;
2053 case MENU_INSERT_ALM: {
2054 if (editable) {
2055 insert_text_at_caret(String::chr(0x061C));
2056 }
2057 } break;
2058 case MENU_INSERT_LRI: {
2059 if (editable) {
2060 insert_text_at_caret(String::chr(0x2066));
2061 }
2062 } break;
2063 case MENU_INSERT_RLI: {
2064 if (editable) {
2065 insert_text_at_caret(String::chr(0x2067));
2066 }
2067 } break;
2068 case MENU_INSERT_FSI: {
2069 if (editable) {
2070 insert_text_at_caret(String::chr(0x2068));
2071 }
2072 } break;
2073 case MENU_INSERT_PDI: {
2074 if (editable) {
2075 insert_text_at_caret(String::chr(0x2069));
2076 }
2077 } break;
2078 case MENU_INSERT_ZWJ: {
2079 if (editable) {
2080 insert_text_at_caret(String::chr(0x200D));
2081 }
2082 } break;
2083 case MENU_INSERT_ZWNJ: {
2084 if (editable) {
2085 insert_text_at_caret(String::chr(0x200C));
2086 }
2087 } break;
2088 case MENU_INSERT_WJ: {
2089 if (editable) {
2090 insert_text_at_caret(String::chr(0x2060));
2091 }
2092 } break;
2093 case MENU_INSERT_SHY: {
2094 if (editable) {
2095 insert_text_at_caret(String::chr(0x00AD));
2096 }
2097 }
2098 }
2099}
2100
2101void LineEdit::set_context_menu_enabled(bool p_enable) {
2102 context_menu_enabled = p_enable;
2103}
2104
2105bool LineEdit::is_context_menu_enabled() {
2106 return context_menu_enabled;
2107}
2108
2109bool LineEdit::is_menu_visible() const {
2110 return menu && menu->is_visible();
2111}
2112
2113PopupMenu *LineEdit::get_menu() const {
2114 if (!menu) {
2115 const_cast<LineEdit *>(this)->_generate_context_menu();
2116 }
2117 return menu;
2118}
2119
2120void LineEdit::_editor_settings_changed() {
2121#ifdef TOOLS_ENABLED
2122 set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
2123 set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval"));
2124#endif
2125}
2126
2127void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) {
2128 expand_to_text_length = p_enabled;
2129 update_minimum_size();
2130 set_caret_column(caret_column);
2131}
2132
2133bool LineEdit::is_expand_to_text_length_enabled() const {
2134 return expand_to_text_length;
2135}
2136
2137void LineEdit::set_clear_button_enabled(bool p_enabled) {
2138 if (clear_button_enabled == p_enabled) {
2139 return;
2140 }
2141 clear_button_enabled = p_enabled;
2142 _fit_to_width();
2143 update_minimum_size();
2144 queue_redraw();
2145}
2146
2147bool LineEdit::is_clear_button_enabled() const {
2148 return clear_button_enabled;
2149}
2150
2151void LineEdit::set_shortcut_keys_enabled(bool p_enabled) {
2152 shortcut_keys_enabled = p_enabled;
2153}
2154
2155bool LineEdit::is_shortcut_keys_enabled() const {
2156 return shortcut_keys_enabled;
2157}
2158
2159void LineEdit::set_virtual_keyboard_enabled(bool p_enable) {
2160 virtual_keyboard_enabled = p_enable;
2161}
2162
2163bool LineEdit::is_virtual_keyboard_enabled() const {
2164 return virtual_keyboard_enabled;
2165}
2166
2167void LineEdit::set_virtual_keyboard_type(VirtualKeyboardType p_type) {
2168 virtual_keyboard_type = p_type;
2169}
2170
2171LineEdit::VirtualKeyboardType LineEdit::get_virtual_keyboard_type() const {
2172 return virtual_keyboard_type;
2173}
2174
2175void LineEdit::set_middle_mouse_paste_enabled(bool p_enabled) {
2176 middle_mouse_paste_enabled = p_enabled;
2177}
2178
2179bool LineEdit::is_middle_mouse_paste_enabled() const {
2180 return middle_mouse_paste_enabled;
2181}
2182
2183void LineEdit::set_selecting_enabled(bool p_enabled) {
2184 if (selecting_enabled == p_enabled) {
2185 return;
2186 }
2187
2188 selecting_enabled = p_enabled;
2189
2190 if (!selecting_enabled) {
2191 deselect();
2192 }
2193}
2194
2195bool LineEdit::is_selecting_enabled() const {
2196 return selecting_enabled;
2197}
2198
2199void LineEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
2200 if (deselect_on_focus_loss_enabled == p_enabled) {
2201 return;
2202 }
2203
2204 deselect_on_focus_loss_enabled = p_enabled;
2205 if (p_enabled && selection.enabled && !has_focus()) {
2206 deselect();
2207 }
2208}
2209
2210bool LineEdit::is_deselect_on_focus_loss_enabled() const {
2211 return deselect_on_focus_loss_enabled;
2212}
2213
2214void LineEdit::set_drag_and_drop_selection_enabled(const bool p_enabled) {
2215 drag_and_drop_selection_enabled = p_enabled;
2216}
2217
2218bool LineEdit::is_drag_and_drop_selection_enabled() const {
2219 return drag_and_drop_selection_enabled;
2220}
2221
2222void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) {
2223 if (right_icon == p_icon) {
2224 return;
2225 }
2226 right_icon = p_icon;
2227 _fit_to_width();
2228 update_minimum_size();
2229 queue_redraw();
2230}
2231
2232Ref<Texture2D> LineEdit::get_right_icon() {
2233 return right_icon;
2234}
2235
2236void LineEdit::set_flat(bool p_enabled) {
2237 if (flat != p_enabled) {
2238 flat = p_enabled;
2239 queue_redraw();
2240 }
2241}
2242
2243bool LineEdit::is_flat() const {
2244 return flat;
2245}
2246
2247void LineEdit::set_select_all_on_focus(bool p_enabled) {
2248 select_all_on_focus = p_enabled;
2249}
2250
2251bool LineEdit::is_select_all_on_focus() const {
2252 return select_all_on_focus;
2253}
2254
2255void LineEdit::clear_pending_select_all_on_focus() {
2256 pending_select_all_on_focus = false;
2257}
2258
2259void LineEdit::_text_changed() {
2260 _emit_text_change();
2261 _clear_redo();
2262}
2263
2264void LineEdit::_emit_text_change() {
2265 emit_signal(SNAME("text_changed"), text);
2266 text_changed_dirty = false;
2267}
2268
2269void LineEdit::_shape() {
2270 const Ref<Font> &font = theme_cache.font;
2271 int font_size = theme_cache.font_size;
2272 if (font.is_null()) {
2273 return;
2274 }
2275
2276 Size2 old_size = TS->shaped_text_get_size(text_rid);
2277 TS->shaped_text_clear(text_rid);
2278
2279 String t;
2280 if (text.length() == 0 && ime_text.length() == 0) {
2281 t = placeholder_translated;
2282 } else if (pass) {
2283 t = secret_character.repeat(text.length() + ime_text.length());
2284 } else {
2285 if (ime_text.length() > 0) {
2286 t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length());
2287 } else {
2288 t = text;
2289 }
2290 }
2291 if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
2292 TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
2293 } else {
2294 TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction);
2295 }
2296 TS->shaped_text_set_preserve_control(text_rid, draw_control_chars);
2297
2298 TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language);
2299 TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
2300
2301 full_width = TS->shaped_text_get_size(text_rid).x;
2302 _fit_to_width();
2303
2304 Size2 size = TS->shaped_text_get_size(text_rid);
2305
2306 if ((expand_to_text_length && old_size.x != size.x) || (old_size.y != size.y)) {
2307 update_minimum_size();
2308 }
2309}
2310
2311void LineEdit::_fit_to_width() {
2312 if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
2313 Ref<StyleBox> style = theme_cache.normal;
2314 int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT);
2315 bool using_placeholder = text.is_empty() && ime_text.is_empty();
2316 bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
2317 if (right_icon.is_valid() || display_clear_icon) {
2318 Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
2319 t_width -= r_icon->get_width();
2320 }
2321 TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width));
2322 }
2323}
2324
2325void LineEdit::_clear_redo() {
2326 _create_undo_state();
2327 if (undo_stack_pos == nullptr) {
2328 return;
2329 }
2330
2331 undo_stack_pos = undo_stack_pos->next();
2332 while (undo_stack_pos) {
2333 List<TextOperation>::Element *elem = undo_stack_pos;
2334 undo_stack_pos = undo_stack_pos->next();
2335 undo_stack.erase(elem);
2336 }
2337 _create_undo_state();
2338}
2339
2340void LineEdit::_clear_undo_stack() {
2341 undo_stack.clear();
2342 undo_stack_pos = nullptr;
2343 _create_undo_state();
2344}
2345
2346void LineEdit::_create_undo_state() {
2347 TextOperation op;
2348 op.text = text;
2349 op.caret_column = caret_column;
2350 op.scroll_offset = scroll_offset;
2351 undo_stack.push_back(op);
2352}
2353
2354Key LineEdit::_get_menu_action_accelerator(const String &p_action) {
2355 const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
2356 if (!events) {
2357 return Key::NONE;
2358 }
2359
2360 // Use first event in the list for the accelerator.
2361 const List<Ref<InputEvent>>::Element *first_event = events->front();
2362 if (!first_event) {
2363 return Key::NONE;
2364 }
2365
2366 const Ref<InputEventKey> event = first_event->get();
2367 if (event.is_null()) {
2368 return Key::NONE;
2369 }
2370
2371 // Use physical keycode if non-zero.
2372 if (event->get_physical_keycode() != Key::NONE) {
2373 return event->get_physical_keycode_with_modifiers();
2374 } else {
2375 return event->get_keycode_with_modifiers();
2376 }
2377}
2378
2379void LineEdit::_generate_context_menu() {
2380 menu = memnew(PopupMenu);
2381 add_child(menu, false, INTERNAL_MODE_FRONT);
2382
2383 menu_dir = memnew(PopupMenu);
2384 menu_dir->set_name("DirMenu");
2385 menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
2386 menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
2387 menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
2388 menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
2389 menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
2390
2391 menu_ctl = memnew(PopupMenu);
2392 menu_ctl->set_name("CTLMenu");
2393 menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
2394 menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
2395 menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
2396 menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
2397 menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
2398 menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
2399 menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
2400 menu_ctl->add_separator();
2401 menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
2402 menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
2403 menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
2404 menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
2405 menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
2406 menu_ctl->add_separator();
2407 menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
2408 menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
2409 menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
2410 menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
2411 menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
2412
2413 menu->add_item(RTR("Cut"), MENU_CUT);
2414 menu->add_item(RTR("Copy"), MENU_COPY);
2415 menu->add_item(RTR("Paste"), MENU_PASTE);
2416 menu->add_separator();
2417 menu->add_item(RTR("Select All"), MENU_SELECT_ALL);
2418 menu->add_item(RTR("Clear"), MENU_CLEAR);
2419 menu->add_separator();
2420 menu->add_item(RTR("Undo"), MENU_UNDO);
2421 menu->add_item(RTR("Redo"), MENU_REDO);
2422 menu->add_separator();
2423 menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR);
2424 menu->add_separator();
2425 menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
2426 menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC);
2427
2428 menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
2429 menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
2430 menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
2431
2432 menu->connect(SNAME("focus_entered"), callable_mp(this, &LineEdit::_validate_caret_can_draw));
2433 menu->connect(SNAME("focus_exited"), callable_mp(this, &LineEdit::_validate_caret_can_draw));
2434}
2435
2436void LineEdit::_update_context_menu() {
2437 if (!menu) {
2438 _generate_context_menu();
2439 }
2440
2441 int idx = -1;
2442
2443#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \
2444 idx = m_menu->get_item_index(m_id); \
2445 if (idx >= 0) { \
2446 m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \
2447 m_menu->set_item_disabled(idx, m_disabled); \
2448 }
2449
2450#define MENU_ITEM_ACTION(m_menu, m_id, m_action) \
2451 idx = m_menu->get_item_index(m_id); \
2452 if (idx >= 0) { \
2453 m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \
2454 }
2455
2456#define MENU_ITEM_DISABLED(m_menu, m_id, m_disabled) \
2457 idx = m_menu->get_item_index(m_id); \
2458 if (idx >= 0) { \
2459 m_menu->set_item_disabled(idx, m_disabled); \
2460 }
2461
2462#define MENU_ITEM_CHECKED(m_menu, m_id, m_checked) \
2463 idx = m_menu->get_item_index(m_id); \
2464 if (idx >= 0) { \
2465 m_menu->set_item_checked(idx, m_checked); \
2466 }
2467
2468 MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable)
2469 MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy")
2470 MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable)
2471 MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selecting_enabled)
2472 MENU_ITEM_DISABLED(menu, MENU_CLEAR, !editable)
2473 MENU_ITEM_ACTION_DISABLED(menu, MENU_UNDO, "ui_undo", !editable || !has_undo())
2474 MENU_ITEM_ACTION_DISABLED(menu, MENU_REDO, "ui_redo", !editable || !has_redo())
2475 MENU_ITEM_CHECKED(menu_dir, MENU_DIR_INHERITED, text_direction == TEXT_DIRECTION_INHERITED)
2476 MENU_ITEM_CHECKED(menu_dir, MENU_DIR_AUTO, text_direction == TEXT_DIRECTION_AUTO)
2477 MENU_ITEM_CHECKED(menu_dir, MENU_DIR_LTR, text_direction == TEXT_DIRECTION_LTR)
2478 MENU_ITEM_CHECKED(menu_dir, MENU_DIR_RTL, text_direction == TEXT_DIRECTION_RTL)
2479 MENU_ITEM_CHECKED(menu, MENU_DISPLAY_UCC, draw_control_chars)
2480 MENU_ITEM_DISABLED(menu, MENU_SUBMENU_INSERT_UCC, !editable)
2481
2482#undef MENU_ITEM_ACTION_DISABLED
2483#undef MENU_ITEM_ACTION
2484#undef MENU_ITEM_DISABLED
2485#undef MENU_ITEM_CHECKED
2486}
2487
2488void LineEdit::_validate_property(PropertyInfo &p_property) const {
2489 if (!caret_blink_enabled && p_property.name == "caret_blink_interval") {
2490 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
2491 }
2492}
2493
2494void LineEdit::_bind_methods() {
2495 ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
2496
2497 ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment);
2498 ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment);
2499
2500 ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear);
2501 ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1));
2502 ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all);
2503 ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect);
2504 ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection);
2505 ClassDB::bind_method(D_METHOD("get_selected_text"), &LineEdit::get_selected_text);
2506 ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column);
2507 ClassDB::bind_method(D_METHOD("get_selection_to_column"), &LineEdit::get_selection_to_column);
2508 ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text);
2509 ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text);
2510 ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars);
2511 ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &LineEdit::set_draw_control_chars);
2512 ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LineEdit::set_text_direction);
2513 ClassDB::bind_method(D_METHOD("get_text_direction"), &LineEdit::get_text_direction);
2514 ClassDB::bind_method(D_METHOD("set_language", "language"), &LineEdit::set_language);
2515 ClassDB::bind_method(D_METHOD("get_language"), &LineEdit::get_language);
2516 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LineEdit::set_structured_text_bidi_override);
2517 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &LineEdit::get_structured_text_bidi_override);
2518 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &LineEdit::set_structured_text_bidi_override_options);
2519 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LineEdit::get_structured_text_bidi_override_options);
2520 ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder);
2521 ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder);
2522 ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column);
2523 ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column);
2524 ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
2525 ClassDB::bind_method(D_METHOD("set_expand_to_text_length_enabled", "enabled"), &LineEdit::set_expand_to_text_length_enabled);
2526 ClassDB::bind_method(D_METHOD("is_expand_to_text_length_enabled"), &LineEdit::is_expand_to_text_length_enabled);
2527 ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enabled"), &LineEdit::set_caret_blink_enabled);
2528 ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &LineEdit::is_caret_blink_enabled);
2529 ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &LineEdit::set_caret_mid_grapheme_enabled);
2530 ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled);
2531 ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed);
2532 ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed);
2533 ClassDB::bind_method(D_METHOD("set_caret_blink_interval", "interval"), &LineEdit::set_caret_blink_interval);
2534 ClassDB::bind_method(D_METHOD("get_caret_blink_interval"), &LineEdit::get_caret_blink_interval);
2535 ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length);
2536 ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length);
2537 ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret);
2538 ClassDB::bind_method(D_METHOD("delete_char_at_caret"), &LineEdit::delete_char);
2539 ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text);
2540 ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable);
2541 ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable);
2542 ClassDB::bind_method(D_METHOD("set_secret", "enabled"), &LineEdit::set_secret);
2543 ClassDB::bind_method(D_METHOD("is_secret"), &LineEdit::is_secret);
2544 ClassDB::bind_method(D_METHOD("set_secret_character", "character"), &LineEdit::set_secret_character);
2545 ClassDB::bind_method(D_METHOD("get_secret_character"), &LineEdit::get_secret_character);
2546 ClassDB::bind_method(D_METHOD("menu_option", "option"), &LineEdit::menu_option);
2547 ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu);
2548 ClassDB::bind_method(D_METHOD("is_menu_visible"), &LineEdit::is_menu_visible);
2549 ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled);
2550 ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled);
2551 ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &LineEdit::set_virtual_keyboard_enabled);
2552 ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &LineEdit::is_virtual_keyboard_enabled);
2553 ClassDB::bind_method(D_METHOD("set_virtual_keyboard_type", "type"), &LineEdit::set_virtual_keyboard_type);
2554 ClassDB::bind_method(D_METHOD("get_virtual_keyboard_type"), &LineEdit::get_virtual_keyboard_type);
2555 ClassDB::bind_method(D_METHOD("set_clear_button_enabled", "enable"), &LineEdit::set_clear_button_enabled);
2556 ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled);
2557 ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &LineEdit::set_shortcut_keys_enabled);
2558 ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled);
2559 ClassDB::bind_method(D_METHOD("set_middle_mouse_paste_enabled", "enable"), &LineEdit::set_middle_mouse_paste_enabled);
2560 ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &LineEdit::is_middle_mouse_paste_enabled);
2561 ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled);
2562 ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled);
2563 ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &LineEdit::set_deselect_on_focus_loss_enabled);
2564 ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &LineEdit::is_deselect_on_focus_loss_enabled);
2565 ClassDB::bind_method(D_METHOD("set_drag_and_drop_selection_enabled", "enable"), &LineEdit::set_drag_and_drop_selection_enabled);
2566 ClassDB::bind_method(D_METHOD("is_drag_and_drop_selection_enabled"), &LineEdit::is_drag_and_drop_selection_enabled);
2567 ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
2568 ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
2569 ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &LineEdit::set_flat);
2570 ClassDB::bind_method(D_METHOD("is_flat"), &LineEdit::is_flat);
2571 ClassDB::bind_method(D_METHOD("set_select_all_on_focus", "enabled"), &LineEdit::set_select_all_on_focus);
2572 ClassDB::bind_method(D_METHOD("is_select_all_on_focus"), &LineEdit::is_select_all_on_focus);
2573
2574 ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
2575 ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
2576 ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text")));
2577
2578 BIND_ENUM_CONSTANT(MENU_CUT);
2579 BIND_ENUM_CONSTANT(MENU_COPY);
2580 BIND_ENUM_CONSTANT(MENU_PASTE);
2581 BIND_ENUM_CONSTANT(MENU_CLEAR);
2582 BIND_ENUM_CONSTANT(MENU_SELECT_ALL);
2583 BIND_ENUM_CONSTANT(MENU_UNDO);
2584 BIND_ENUM_CONSTANT(MENU_REDO);
2585 BIND_ENUM_CONSTANT(MENU_SUBMENU_TEXT_DIR);
2586 BIND_ENUM_CONSTANT(MENU_DIR_INHERITED);
2587 BIND_ENUM_CONSTANT(MENU_DIR_AUTO);
2588 BIND_ENUM_CONSTANT(MENU_DIR_LTR);
2589 BIND_ENUM_CONSTANT(MENU_DIR_RTL);
2590 BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC);
2591 BIND_ENUM_CONSTANT(MENU_SUBMENU_INSERT_UCC);
2592 BIND_ENUM_CONSTANT(MENU_INSERT_LRM);
2593 BIND_ENUM_CONSTANT(MENU_INSERT_RLM);
2594 BIND_ENUM_CONSTANT(MENU_INSERT_LRE);
2595 BIND_ENUM_CONSTANT(MENU_INSERT_RLE);
2596 BIND_ENUM_CONSTANT(MENU_INSERT_LRO);
2597 BIND_ENUM_CONSTANT(MENU_INSERT_RLO);
2598 BIND_ENUM_CONSTANT(MENU_INSERT_PDF);
2599 BIND_ENUM_CONSTANT(MENU_INSERT_ALM);
2600 BIND_ENUM_CONSTANT(MENU_INSERT_LRI);
2601 BIND_ENUM_CONSTANT(MENU_INSERT_RLI);
2602 BIND_ENUM_CONSTANT(MENU_INSERT_FSI);
2603 BIND_ENUM_CONSTANT(MENU_INSERT_PDI);
2604 BIND_ENUM_CONSTANT(MENU_INSERT_ZWJ);
2605 BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ);
2606 BIND_ENUM_CONSTANT(MENU_INSERT_WJ);
2607 BIND_ENUM_CONSTANT(MENU_INSERT_SHY);
2608 BIND_ENUM_CONSTANT(MENU_MAX);
2609
2610 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_DEFAULT);
2611 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_MULTILINE);
2612 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_NUMBER);
2613 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_NUMBER_DECIMAL);
2614 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_PHONE);
2615 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_EMAIL_ADDRESS);
2616 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_PASSWORD);
2617 BIND_ENUM_CONSTANT(KEYBOARD_TYPE_URL);
2618
2619 ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
2620 ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
2621 ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
2622 ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length");
2623 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
2624 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
2625 ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
2626 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled");
2627 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
2628 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
2629 ADD_PROPERTY(PropertyInfo(Variant::INT, "virtual_keyboard_type", PROPERTY_HINT_ENUM, "Default,Multiline,Number,Decimal,Phone,Email,Password,URL"), "set_virtual_keyboard_type", "get_virtual_keyboard_type");
2630 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
2631 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
2632 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
2633 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
2634 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
2635 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled");
2636 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
2637 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
2638 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
2639 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus");
2640
2641 ADD_GROUP("Caret", "caret_");
2642 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
2643 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_interval", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_interval", "get_caret_blink_interval");
2644 ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column");
2645 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed");
2646 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled");
2647
2648 ADD_GROUP("BiDi", "");
2649 ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
2650 ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
2651 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");
2652 ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
2653
2654 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LineEdit, normal);
2655 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LineEdit, read_only);
2656 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LineEdit, focus);
2657
2658 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, LineEdit, font);
2659 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, LineEdit, font_size);
2660 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, font_color);
2661 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, font_uneditable_color);
2662 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, font_selected_color);
2663 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, LineEdit, font_outline_size, "outline_size");
2664 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, font_outline_color);
2665 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, font_placeholder_color);
2666 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, LineEdit, caret_width);
2667 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, caret_color);
2668 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, LineEdit, minimum_character_width);
2669 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, selection_color);
2670
2671 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, LineEdit, clear_icon, "clear");
2672 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, clear_button_color);
2673 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, clear_button_color_pressed);
2674}
2675
2676LineEdit::LineEdit(const String &p_placeholder) {
2677 text_rid = TS->create_shaped_text();
2678 _create_undo_state();
2679
2680 deselect();
2681 set_focus_mode(FOCUS_ALL);
2682 set_default_cursor_shape(CURSOR_IBEAM);
2683 set_mouse_filter(MOUSE_FILTER_STOP);
2684 set_process_unhandled_key_input(true);
2685
2686 set_caret_blink_enabled(false);
2687
2688 set_placeholder(p_placeholder);
2689
2690 set_editable(true); // Initialize to opposite first, so we get past the early-out in set_editable.
2691}
2692
2693LineEdit::~LineEdit() {
2694 TS->free_rid(text_rid);
2695}
2696