2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* text_edit.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "text_edit.h"
2024-01-22 23:27:19 +00:00
# include "text_edit.compat.inc"
2017-08-27 19:07:15 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
2020-04-28 13:19:37 +00:00
# include "core/input/input.h"
2020-12-07 11:32:00 +00:00
# include "core/input/input_map.h"
2020-11-07 22:33:38 +00:00
# include "core/object/script_language.h"
2018-09-11 16:13:45 +00:00
# include "core/os/keyboard.h"
# include "core/os/os.h"
2021-08-27 19:05:05 +00:00
# include "core/string/string_builder.h"
2020-09-18 07:36:10 +00:00
# include "core/string/translation.h"
2023-09-08 19:00:10 +00:00
# include "scene/gui/label.h"
2020-03-04 01:51:12 +00:00
# include "scene/main/window.h"
2023-09-08 19:00:10 +00:00
# include "scene/theme/theme_db.h"
2014-02-10 01:10:30 +00:00
2021-07-10 10:41:38 +00:00
///////////////////////////////////////////////////////////////////////////////
/// TEXT ///
2020-07-25 00:15:23 +00:00
///////////////////////////////////////////////////////////////////////////////
2014-02-10 01:10:30 +00:00
void TextEdit : : Text : : set_font ( const Ref < Font > & p_font ) {
2021-08-12 18:47:29 +00:00
if ( font = = p_font ) {
return ;
}
2015-01-02 18:08:40 +00:00
font = p_font ;
2021-08-12 18:47:29 +00:00
is_dirty = true ;
2014-02-10 01:10:30 +00:00
}
2020-09-18 07:36:10 +00:00
void TextEdit : : Text : : set_font_size ( int p_font_size ) {
2021-08-12 18:47:29 +00:00
if ( font_size = = p_font_size ) {
return ;
}
2020-09-18 07:36:10 +00:00
font_size = p_font_size ;
2021-08-12 18:47:29 +00:00
is_dirty = true ;
2020-09-18 07:36:10 +00:00
}
2021-06-15 14:05:01 +00:00
void TextEdit : : Text : : set_tab_size ( int p_tab_size ) {
2021-08-27 19:05:05 +00:00
if ( tab_size = = p_tab_size ) {
return ;
}
2021-06-15 14:05:01 +00:00
tab_size = p_tab_size ;
2021-08-27 19:05:05 +00:00
tab_size_dirty = true ;
2021-06-15 14:05:01 +00:00
}
int TextEdit : : Text : : get_tab_size ( ) const {
return tab_size ;
2014-02-10 01:10:30 +00:00
}
2024-02-19 11:41:12 +00:00
void TextEdit : : Text : : set_indent_wrapped_lines ( bool p_enabled ) {
if ( indent_wrapped_lines = = p_enabled ) {
return ;
}
indent_wrapped_lines = p_enabled ;
tab_size_dirty = true ;
}
bool TextEdit : : Text : : is_indent_wrapped_lines ( ) const {
return indent_wrapped_lines ;
}
2021-07-10 10:41:38 +00:00
void TextEdit : : Text : : set_direction_and_language ( TextServer : : Direction p_direction , const String & p_language ) {
2021-08-12 18:47:29 +00:00
if ( direction = = p_direction & & language = = p_language ) {
return ;
}
2020-09-18 07:36:10 +00:00
direction = p_direction ;
language = p_language ;
2021-08-12 18:47:29 +00:00
is_dirty = true ;
2020-09-18 07:36:10 +00:00
}
2016-03-08 23:00:52 +00:00
2021-10-08 21:26:13 +00:00
void TextEdit : : Text : : set_draw_control_chars ( bool p_enabled ) {
if ( draw_control_chars = = p_enabled ) {
2021-08-12 18:47:29 +00:00
return ;
}
2021-10-08 21:26:13 +00:00
draw_control_chars = p_enabled ;
2021-08-12 18:47:29 +00:00
is_dirty = true ;
2020-09-18 07:36:10 +00:00
}
2016-03-08 23:00:52 +00:00
2021-03-13 14:09:49 +00:00
int TextEdit : : Text : : get_line_width ( int p_line , int p_wrap_index ) const {
2020-09-18 07:36:10 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2021-03-13 14:09:49 +00:00
if ( p_wrap_index ! = - 1 ) {
return text [ p_line ] . data_buf - > get_line_width ( p_wrap_index ) ;
}
2020-09-18 07:36:10 +00:00
return text [ p_line ] . data_buf - > get_size ( ) . x ;
}
2016-03-08 23:00:52 +00:00
2021-08-27 19:05:05 +00:00
int TextEdit : : Text : : get_line_height ( ) const {
return line_height ;
2014-02-10 01:10:30 +00:00
}
2020-09-18 07:36:10 +00:00
void TextEdit : : Text : : set_width ( float p_width ) {
width = p_width ;
}
2022-01-23 20:07:35 +00:00
float TextEdit : : Text : : get_width ( ) const {
return width ;
}
2023-03-12 11:21:00 +00:00
void TextEdit : : Text : : set_brk_flags ( BitField < TextServer : : LineBreakFlag > p_flags ) {
brk_flags = p_flags ;
}
BitField < TextServer : : LineBreakFlag > TextEdit : : Text : : get_brk_flags ( ) const {
return brk_flags ;
}
2020-09-18 07:36:10 +00:00
int TextEdit : : Text : : get_line_wrap_amount ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2016-03-08 23:00:52 +00:00
2020-09-18 07:36:10 +00:00
return text [ p_line ] . data_buf - > get_line_count ( ) - 1 ;
}
Vector < Vector2i > TextEdit : : Text : : get_line_wrap_ranges ( int p_line ) const {
Vector < Vector2i > ret ;
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , ret ) ;
for ( int i = 0 ; i < text [ p_line ] . data_buf - > get_line_count ( ) ; i + + ) {
ret . push_back ( text [ p_line ] . data_buf - > get_line_range ( i ) ) ;
2015-01-02 18:08:40 +00:00
}
2020-09-18 07:36:10 +00:00
return ret ;
}
2016-03-08 23:00:52 +00:00
2020-09-18 07:36:10 +00:00
const Ref < TextParagraph > TextEdit : : Text : : get_line_data ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Ref < TextParagraph > ( ) ) ;
return text [ p_line ] . data_buf ;
2014-02-10 01:10:30 +00:00
}
2020-09-18 07:36:10 +00:00
_FORCE_INLINE_ const String & TextEdit : : Text : : operator [ ] ( int p_line ) const {
return text [ p_line ] . data ;
}
2021-08-27 19:05:05 +00:00
void TextEdit : : Text : : _calculate_line_height ( ) {
int height = 0 ;
2021-12-08 15:29:40 +00:00
for ( const Line & l : text ) {
2021-08-27 19:05:05 +00:00
// Found another line with the same height...nothing to update.
2021-12-08 15:29:40 +00:00
if ( l . height = = line_height ) {
2021-08-27 19:05:05 +00:00
height = line_height ;
break ;
}
2021-12-08 15:29:40 +00:00
height = MAX ( height , l . height ) ;
2021-08-27 19:05:05 +00:00
}
line_height = height ;
}
void TextEdit : : Text : : _calculate_max_line_width ( ) {
2022-09-29 09:53:28 +00:00
int line_width = 0 ;
2021-12-08 15:29:40 +00:00
for ( const Line & l : text ) {
if ( l . hidden ) {
2021-08-27 19:05:05 +00:00
continue ;
}
// Found another line with the same width...nothing to update.
2021-12-08 15:29:40 +00:00
if ( l . width = = max_width ) {
2022-09-29 09:53:28 +00:00
line_width = max_width ;
2021-08-27 19:05:05 +00:00
break ;
}
2022-09-29 09:53:28 +00:00
line_width = MAX ( line_width , l . width ) ;
2021-08-27 19:05:05 +00:00
}
2022-09-29 09:53:28 +00:00
max_width = line_width ;
2021-08-27 19:05:05 +00:00
}
2022-01-20 07:30:42 +00:00
void TextEdit : : Text : : invalidate_cache ( int p_line , int p_column , bool p_text_changed , const String & p_ime_text , const Array & p_bidi_override ) {
2018-01-26 01:41:17 +00:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2022-05-09 09:47:10 +00:00
if ( font . is_null ( ) ) {
2020-09-18 07:36:10 +00:00
return ; // Not in tree?
}
2018-01-26 01:41:17 +00:00
2022-01-20 07:30:42 +00:00
if ( p_text_changed ) {
text . write [ p_line ] . data_buf - > clear ( ) ;
}
2024-02-19 11:41:12 +00:00
BitField < TextServer : : LineBreakFlag > flags = brk_flags ;
if ( indent_wrapped_lines ) {
flags . set_flag ( TextServer : : BREAK_TRIM_INDENT ) ;
}
2020-09-18 07:36:10 +00:00
text . write [ p_line ] . data_buf - > set_width ( width ) ;
text . write [ p_line ] . data_buf - > set_direction ( ( TextServer : : Direction ) direction ) ;
2024-02-19 11:41:12 +00:00
text . write [ p_line ] . data_buf - > set_break_flags ( flags ) ;
2020-09-18 07:36:10 +00:00
text . write [ p_line ] . data_buf - > set_preserve_control ( draw_control_chars ) ;
2024-05-29 11:06:43 +00:00
text . write [ p_line ] . data_buf - > set_custom_punctuation ( get_enabled_word_separators ( ) ) ;
2020-09-18 07:36:10 +00:00
if ( p_ime_text . length ( ) > 0 ) {
2022-01-20 07:30:42 +00:00
if ( p_text_changed ) {
2022-05-09 09:47:10 +00:00
text . write [ p_line ] . data_buf - > add_string ( p_ime_text , font , font_size , language ) ;
2022-01-20 07:30:42 +00:00
}
2020-12-15 12:04:21 +00:00
if ( ! p_bidi_override . is_empty ( ) ) {
2020-09-18 07:36:10 +00:00
TS - > shaped_text_set_bidi_override ( text . write [ p_line ] . data_buf - > get_rid ( ) , p_bidi_override ) ;
}
} else {
2022-01-20 07:30:42 +00:00
if ( p_text_changed ) {
2022-05-09 09:47:10 +00:00
text . write [ p_line ] . data_buf - > add_string ( text [ p_line ] . data , font , font_size , language ) ;
2022-01-20 07:30:42 +00:00
}
2020-12-15 12:04:21 +00:00
if ( ! text [ p_line ] . bidi_override . is_empty ( ) ) {
2020-09-18 07:36:10 +00:00
TS - > shaped_text_set_bidi_override ( text . write [ p_line ] . data_buf - > get_rid ( ) , text [ p_line ] . bidi_override ) ;
}
}
2018-01-26 01:41:17 +00:00
2022-01-20 07:30:42 +00:00
if ( ! p_text_changed ) {
RID r = text . write [ p_line ] . data_buf - > get_rid ( ) ;
int spans = TS - > shaped_get_span_count ( r ) ;
for ( int i = 0 ; i < spans ; i + + ) {
2022-05-09 09:47:10 +00:00
TS - > shaped_set_span_update_font ( r , i , font - > get_rids ( ) , font_size , font - > get_opentype_features ( ) ) ;
}
2022-01-20 07:30:42 +00:00
}
2020-09-18 07:36:10 +00:00
// Apply tab align.
2021-06-15 14:05:01 +00:00
if ( tab_size > 0 ) {
2020-09-18 07:36:10 +00:00
Vector < float > tabs ;
2022-05-09 09:47:10 +00:00
tabs . push_back ( font - > get_char_size ( ' ' , font_size ) . width * tab_size ) ;
2020-09-18 07:36:10 +00:00
text . write [ p_line ] . data_buf - > tab_align ( tabs ) ;
}
2021-08-27 19:05:05 +00:00
// Update height.
const int old_height = text . write [ p_line ] . height ;
const int wrap_amount = get_line_wrap_amount ( p_line ) ;
2021-12-08 15:29:40 +00:00
int height = font_height ;
2021-08-27 19:05:05 +00:00
for ( int i = 0 ; i < = wrap_amount ; i + + ) {
height = MAX ( height , text [ p_line ] . data_buf - > get_line_size ( i ) . y ) ;
}
text . write [ p_line ] . height = height ;
2024-02-23 19:49:13 +00:00
// If this line has shrunk, this may no longer the tallest line.
2021-08-27 19:05:05 +00:00
if ( old_height = = line_height & & height < line_height ) {
_calculate_line_height ( ) ;
} else {
line_height = MAX ( height , line_height ) ;
}
// Update width.
const int old_width = text . write [ p_line ] . width ;
2022-09-29 09:53:28 +00:00
int line_width = get_line_width ( p_line ) ;
text . write [ p_line ] . width = line_width ;
2021-08-27 19:05:05 +00:00
2024-02-23 19:49:13 +00:00
// If this line has shrunk, this may no longer the longest line.
2022-09-29 09:53:28 +00:00
if ( old_width = = max_width & & line_width < max_width ) {
2021-08-27 19:05:05 +00:00
_calculate_max_line_width ( ) ;
} else if ( ! is_hidden ( p_line ) ) {
2022-09-29 09:53:28 +00:00
max_width = MAX ( line_width , max_width ) ;
2021-08-27 19:05:05 +00:00
}
2018-01-26 01:41:17 +00:00
}
2020-09-18 07:36:10 +00:00
void TextEdit : : Text : : invalidate_all_lines ( ) {
2018-01-26 01:41:17 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2024-02-19 11:41:12 +00:00
BitField < TextServer : : LineBreakFlag > flags = brk_flags ;
if ( indent_wrapped_lines ) {
flags . set_flag ( TextServer : : BREAK_TRIM_INDENT ) ;
}
2020-09-18 07:36:10 +00:00
text . write [ i ] . data_buf - > set_width ( width ) ;
2024-02-19 11:41:12 +00:00
text . write [ i ] . data_buf - > set_break_flags ( flags ) ;
2024-05-29 11:06:43 +00:00
text . write [ i ] . data_buf - > set_custom_punctuation ( get_enabled_word_separators ( ) ) ;
2021-08-27 19:05:05 +00:00
if ( tab_size_dirty ) {
if ( tab_size > 0 ) {
Vector < float > tabs ;
2022-05-09 09:47:10 +00:00
tabs . push_back ( font - > get_char_size ( ' ' , font_size ) . width * tab_size ) ;
2021-08-27 19:05:05 +00:00
text . write [ i ] . data_buf - > tab_align ( tabs ) ;
}
2020-09-18 07:36:10 +00:00
}
2023-03-12 11:21:00 +00:00
text . write [ i ] . width = get_line_width ( i ) ;
2018-01-26 01:41:17 +00:00
}
2023-03-12 11:21:00 +00:00
tab_size_dirty = false ;
2021-08-27 19:05:05 +00:00
2024-05-27 18:56:48 +00:00
max_width = - 1 ;
2023-03-12 11:21:00 +00:00
_calculate_max_line_width ( ) ;
2018-01-26 01:41:17 +00:00
}
2022-01-20 07:30:42 +00:00
void TextEdit : : Text : : invalidate_font ( ) {
if ( ! is_dirty ) {
return ;
}
max_width = - 1 ;
line_height = - 1 ;
2022-05-09 09:47:10 +00:00
if ( font . is_valid ( ) & & font_size > 0 ) {
2022-01-20 07:30:42 +00:00
font_height = font - > get_height ( font_size ) ;
}
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
invalidate_cache ( i , - 1 , false ) ;
}
is_dirty = false ;
}
2020-09-18 07:36:10 +00:00
void TextEdit : : Text : : invalidate_all ( ) {
2021-08-12 18:47:29 +00:00
if ( ! is_dirty ) {
return ;
}
2021-12-08 15:29:40 +00:00
max_width = - 1 ;
line_height = - 1 ;
2022-05-09 09:47:10 +00:00
if ( font . is_valid ( ) & & font_size > 0 ) {
2021-12-08 15:29:40 +00:00
font_height = font - > get_height ( font_size ) ;
}
2018-01-26 01:41:17 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2022-01-20 07:30:42 +00:00
invalidate_cache ( i , - 1 , true ) ;
2018-01-26 01:41:17 +00:00
}
2021-08-12 18:47:29 +00:00
is_dirty = false ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : Text : : clear ( ) {
2017-01-14 17:03:38 +00:00
text . clear ( ) ;
2021-12-08 15:29:40 +00:00
max_width = - 1 ;
line_height = - 1 ;
Line line ;
line . gutters . resize ( gutter_count ) ;
line . data = " " ;
text . insert ( 0 , line ) ;
2022-01-20 07:30:42 +00:00
invalidate_cache ( 0 , - 1 , true ) ;
2014-02-10 01:10:30 +00:00
}
2021-08-27 19:05:05 +00:00
int TextEdit : : Text : : get_max_width ( ) const {
return max_width ;
2014-02-10 01:10:30 +00:00
}
2021-08-27 21:19:51 +00:00
void TextEdit : : Text : : set ( int p_line , const String & p_text , const Array & p_bidi_override ) {
2015-01-02 18:08:40 +00:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2016-03-08 23:00:52 +00:00
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . data = p_text ;
2020-09-18 07:36:10 +00:00
text . write [ p_line ] . bidi_override = p_bidi_override ;
2022-01-20 07:30:42 +00:00
invalidate_cache ( p_line , - 1 , true ) ;
2014-02-10 01:10:30 +00:00
}
2021-12-08 15:29:40 +00:00
void TextEdit : : Text : : insert ( int p_at , const Vector < String > & p_text , const Vector < Array > & p_bidi_override ) {
int new_line_count = p_text . size ( ) - 1 ;
if ( new_line_count > 0 ) {
text . resize ( text . size ( ) + new_line_count ) ;
for ( int i = ( text . size ( ) - 1 ) ; i > p_at ; i - - ) {
if ( ( i - new_line_count ) < = 0 ) {
break ;
}
text . write [ i ] = text [ i - new_line_count ] ;
}
}
2020-09-18 07:36:10 +00:00
2021-12-08 15:29:40 +00:00
for ( int i = 0 ; i < p_text . size ( ) ; i + + ) {
if ( i = = 0 ) {
set ( p_at + i , p_text [ i ] , p_bidi_override [ i ] ) ;
continue ;
}
Line line ;
line . gutters . resize ( gutter_count ) ;
line . data = p_text [ i ] ;
line . bidi_override = p_bidi_override [ i ] ;
text . write [ p_at + i ] = line ;
2022-01-20 07:30:42 +00:00
invalidate_cache ( p_at + i , - 1 , true ) ;
2021-12-08 15:29:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2021-12-08 15:29:40 +00:00
void TextEdit : : Text : : remove_range ( int p_from_line , int p_to_line ) {
if ( p_from_line = = p_to_line ) {
return ;
}
2021-08-27 19:05:05 +00:00
2021-12-08 15:29:40 +00:00
bool dirty_height = false ;
bool dirty_width = false ;
for ( int i = p_from_line ; i < p_to_line ; i + + ) {
if ( ! dirty_height & & text [ i ] . height = = line_height ) {
dirty_height = true ;
}
if ( ! dirty_width & & text [ i ] . width = = max_width ) {
dirty_width = true ;
}
if ( dirty_height & & dirty_width ) {
break ;
}
}
int diff = ( p_to_line - p_from_line ) ;
for ( int i = p_to_line ; i < text . size ( ) - 1 ; i + + ) {
text . write [ ( i - diff ) + 1 ] = text [ i + 1 ] ;
}
text . resize ( text . size ( ) - diff ) ;
2021-08-27 19:05:05 +00:00
2021-12-08 15:29:40 +00:00
if ( dirty_height ) {
2024-05-27 18:56:48 +00:00
line_height = - 1 ;
2021-08-27 19:05:05 +00:00
_calculate_line_height ( ) ;
}
2021-12-08 15:29:40 +00:00
if ( dirty_width ) {
2024-05-27 18:56:48 +00:00
max_width = - 1 ;
2021-08-27 19:05:05 +00:00
_calculate_max_line_width ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2020-07-25 00:15:23 +00:00
void TextEdit : : Text : : add_gutter ( int p_at ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( p_at < 0 | | p_at > gutter_count ) {
text . write [ i ] . gutters . push_back ( Gutter ( ) ) ;
} else {
text . write [ i ] . gutters . insert ( p_at , Gutter ( ) ) ;
}
}
gutter_count + + ;
}
void TextEdit : : Text : : remove_gutter ( int p_gutter ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2021-07-03 22:17:03 +00:00
text . write [ i ] . gutters . remove_at ( p_gutter ) ;
2020-07-25 00:15:23 +00:00
}
gutter_count - - ;
}
void TextEdit : : Text : : move_gutters ( int p_from_line , int p_to_line ) {
text . write [ p_to_line ] . gutters = text [ p_from_line ] . gutters ;
text . write [ p_from_line ] . gutters . clear ( ) ;
text . write [ p_from_line ] . gutters . resize ( gutter_count ) ;
}
2024-05-29 11:06:43 +00:00
void TextEdit : : Text : : set_use_default_word_separators ( bool p_enabled ) {
if ( use_default_word_separators = = p_enabled ) {
return ;
}
use_default_word_separators = p_enabled ;
invalidate_all_lines ( ) ;
}
void TextEdit : : Text : : set_use_custom_word_separators ( bool p_enabled ) {
if ( use_custom_word_separators = = p_enabled ) {
return ;
}
use_custom_word_separators = p_enabled ;
invalidate_all_lines ( ) ;
}
bool TextEdit : : Text : : is_default_word_separators_enabled ( ) const {
return use_default_word_separators ;
}
bool TextEdit : : Text : : is_custom_word_separators_enabled ( ) const {
return use_custom_word_separators ;
}
String TextEdit : : Text : : get_custom_word_separators ( ) const {
return custom_word_separators ;
}
String TextEdit : : Text : : get_default_word_separators ( ) const {
2024-06-27 10:26:02 +00:00
String concat_separators = " ! \" #$%&'()*+,-./:;<=>?@[ \\ ]^`{|}~ " ;
2024-05-29 11:06:43 +00:00
for ( char32_t ch = 0x2000 ; ch < = 0x206F ; + + ch ) { // General punctuation block.
concat_separators + = ch ;
}
for ( char32_t ch = 0x3000 ; ch < = 0x303F ; + + ch ) { // CJK punctuation block.
concat_separators + = ch ;
}
return concat_separators ;
}
// Get default and/or custom word separators depending on the option enabled.
String TextEdit : : Text : : get_enabled_word_separators ( ) const {
String all_separators ;
if ( use_default_word_separators ) {
all_separators + = get_default_word_separators ( ) ;
}
if ( use_custom_word_separators ) {
all_separators + = get_custom_word_separators ( ) ;
}
return all_separators ;
}
2021-07-10 10:41:38 +00:00
///////////////////////////////////////////////////////////////////////////////
/// TEXT EDIT ///
///////////////////////////////////////////////////////////////////////////////
2020-09-18 07:36:10 +00:00
2014-02-10 01:10:30 +00:00
void TextEdit : : _notification ( int p_what ) {
2014-12-17 01:31:57 +00:00
switch ( p_what ) {
2023-04-03 16:01:10 +00:00
case NOTIFICATION_POSTINITIALIZE : {
_update_caches ( ) ;
} break ;
2014-12-17 01:31:57 +00:00
case NOTIFICATION_ENTER_TREE : {
_update_caches ( ) ;
2021-07-08 17:35:56 +00:00
if ( caret_pos_dirty ) {
2023-12-18 14:46:56 +00:00
callable_mp ( this , & TextEdit : : _emit_caret_changed ) . call_deferred ( ) ;
2020-05-14 14:41:43 +00:00
}
if ( text_changed_dirty ) {
2024-01-22 23:26:16 +00:00
callable_mp ( this , & TextEdit : : _emit_text_changed ) . call_deferred ( ) ;
2020-05-14 14:41:43 +00:00
}
2021-07-09 10:52:49 +00:00
_update_wrap_at_column ( true ) ;
2014-12-17 01:31:57 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2014-12-17 01:31:57 +00:00
case NOTIFICATION_RESIZED : {
2018-01-26 01:41:17 +00:00
_update_scrollbars ( ) ;
2021-07-09 10:52:49 +00:00
_update_wrap_at_column ( ) ;
2014-12-17 01:31:57 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2019-09-02 04:17:11 +00:00
case NOTIFICATION_VISIBILITY_CHANGED : {
if ( is_visible ( ) ) {
2023-12-18 14:46:56 +00:00
callable_mp ( this , & TextEdit : : _update_scrollbars ) . call_deferred ( ) ;
callable_mp ( this , & TextEdit : : _update_wrap_at_column ) . call_deferred ( false ) ;
2019-09-02 04:17:11 +00:00
}
} break ;
2022-02-15 17:06:48 +00:00
2020-09-18 07:36:10 +00:00
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
case NOTIFICATION_TRANSLATION_CHANGED :
2014-12-17 01:31:57 +00:00
case NOTIFICATION_THEME_CHANGED : {
2022-07-08 12:38:30 +00:00
if ( is_inside_tree ( ) ) {
_update_caches ( ) ;
_update_wrap_at_column ( true ) ;
}
2016-05-29 14:37:26 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2020-06-29 23:47:18 +00:00
case NOTIFICATION_WM_WINDOW_FOCUS_IN : {
2016-06-19 15:11:16 +00:00
window_has_focus = true ;
draw_caret = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2016-06-19 15:11:16 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2020-06-29 23:47:18 +00:00
case NOTIFICATION_WM_WINDOW_FOCUS_OUT : {
2016-06-19 15:11:16 +00:00
window_has_focus = false ;
draw_caret = false ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2016-06-19 15:11:16 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2018-04-11 07:28:14 +00:00
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
2018-01-26 01:41:17 +00:00
if ( scrolling & & get_v_scroll ( ) ! = target_v_scroll ) {
double target_y = target_v_scroll - get_v_scroll ( ) ;
2022-10-05 08:56:19 +00:00
double dist = abs ( target_y ) ;
Fix misc. source comment typos
Found using `codespell -q 3 -S ./thirdparty,*.po -L ang,ba,cas,dof,doubleclick,fave,hist,leapyear,lod,nd,numer,ois,paket,seeked,sinc,switchs,te,uint -D ~/Projects/codespell/codespell_lib/data/dictionary.txt `
2019-09-19 18:36:39 +00:00
// To ensure minimap is responsive override the speed setting.
2019-08-11 18:31:19 +00:00
double vel = ( ( target_y / dist ) * ( ( minimap_clicked ) ? 3000 : v_scroll_speed ) ) * get_physics_process_delta_time ( ) ;
2017-08-19 14:23:45 +00:00
2023-03-08 22:00:27 +00:00
// Prevent small velocities from blocking scrolling.
2022-02-12 13:40:49 +00:00
if ( Math : : abs ( vel ) < v_scroll - > get_step ( ) ) {
vel = v_scroll - > get_step ( ) * SIGN ( vel ) ;
}
2017-09-02 12:17:13 +00:00
if ( Math : : abs ( vel ) > = dist ) {
2018-01-26 01:41:17 +00:00
set_v_scroll ( target_v_scroll ) ;
2017-08-19 14:23:45 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( false ) ;
2017-08-19 14:23:45 +00:00
} else {
2018-01-26 01:41:17 +00:00
set_v_scroll ( get_v_scroll ( ) + vel ) ;
2017-08-19 14:23:45 +00:00
}
} else {
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( false ) ;
2017-08-19 14:23:45 +00:00
}
} break ;
2022-02-15 17:06:48 +00:00
2014-12-17 01:31:57 +00:00
case NOTIFICATION_DRAW : {
2019-04-12 02:21:48 +00:00
if ( first_draw ) {
2021-07-08 17:35:56 +00:00
// Size may not be the final one, so attempts to ensure caret was visible may have failed.
adjust_viewport_to_caret ( ) ;
2019-04-12 02:21:48 +00:00
first_draw = false ;
}
2020-05-03 16:08:15 +00:00
/* Prevent the resource getting lost between the editor and game. */
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
if ( syntax_highlighter . is_valid ( ) & & syntax_highlighter - > get_text_edit ( ) ! = this ) {
syntax_highlighter - > set_text_edit ( this ) ;
}
}
2018-08-20 11:36:34 +00:00
Size2 size = get_size ( ) ;
2020-09-18 07:36:10 +00:00
bool rtl = is_layout_rtl ( ) ;
2021-07-16 21:36:05 +00:00
if ( ( ! has_focus ( ) & & ! ( menu & & menu - > has_focus ( ) ) ) | | ! window_has_focus ) {
2016-06-19 15:11:16 +00:00
draw_caret = false ;
}
2014-12-17 01:31:57 +00:00
_update_scrollbars ( ) ;
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
RID ci = get_canvas_item ( ) ;
2023-04-03 16:01:10 +00:00
int xmargin_beg = theme_cache . style_normal - > get_margin ( SIDE_LEFT ) + gutters_width + gutter_padding ;
2019-08-11 18:31:19 +00:00
2023-04-03 16:01:10 +00:00
int xmargin_end = size . width - theme_cache . style_normal - > get_margin ( SIDE_RIGHT ) ;
2021-07-09 16:27:09 +00:00
if ( draw_minimap ) {
xmargin_end - = minimap_width ;
}
2019-08-18 15:56:24 +00:00
// Let's do it easy for now.
2023-04-03 16:01:10 +00:00
theme_cache . style_normal - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2023-04-03 16:01:10 +00:00
theme_cache . style_readonly - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2023-02-07 23:02:48 +00:00
draw_caret = is_drawing_caret_when_editable_disabled ( ) ;
2017-11-30 03:53:15 +00:00
}
2020-05-14 14:41:43 +00:00
if ( has_focus ( ) ) {
2023-04-03 16:01:10 +00:00
theme_cache . style_focus - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-09 16:27:09 +00:00
int visible_rows = get_visible_line_count ( ) + 1 ;
2016-03-08 23:00:52 +00:00
2023-04-03 16:01:10 +00:00
if ( theme_cache . background_color . a > 0.01 ) {
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( ) , get_size ( ) ) , theme_cache . background_color ) ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
Vector < BraceMatchingData > brace_matching ;
if ( highlight_matching_braces_enabled ) {
2024-01-22 23:26:16 +00:00
brace_matching . resize ( get_caret_count ( ) ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:26:16 +00:00
for ( int caret = 0 ; caret < get_caret_count ( ) ; caret + + ) {
2022-06-03 16:47:37 +00:00
if ( get_caret_line ( caret ) < 0 | | get_caret_line ( caret ) > = text . size ( ) | | get_caret_column ( caret ) < 0 ) {
continue ;
2014-12-17 04:53:34 +00:00
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( get_caret_column ( caret ) < text [ get_caret_line ( caret ) ] . length ( ) ) {
// Check for open.
char32_t c = text [ get_caret_line ( caret ) ] [ get_caret_column ( caret ) ] ;
char32_t closec = 0 ;
if ( c = = ' [ ' ) {
closec = ' ] ' ;
} else if ( c = = ' { ' ) {
closec = ' } ' ;
} else if ( c = = ' ( ' ) {
closec = ' ) ' ;
}
if ( closec ! = 0 ) {
int stack = 1 ;
for ( int i = get_caret_line ( caret ) ; i < text . size ( ) ; i + + ) {
int from = i = = get_caret_line ( caret ) ? get_caret_column ( caret ) + 1 : 0 ;
for ( int j = from ; j < text [ i ] . length ( ) ; j + + ) {
char32_t cc = text [ i ] [ j ] ;
// Ignore any brackets inside a string.
if ( cc = = ' " ' | | cc = = ' \' ' ) {
char32_t quotation = cc ;
do {
j + + ;
if ( ! ( j < text [ i ] . length ( ) ) ) {
break ;
2015-02-20 00:34:04 +00:00
}
2022-06-03 16:47:37 +00:00
cc = text [ i ] [ j ] ;
// Skip over escaped quotation marks inside strings.
if ( cc = = ' \\ ' ) {
bool escaped = true ;
while ( j + 1 < text [ i ] . length ( ) & & text [ i ] [ j + 1 ] = = ' \\ ' ) {
escaped = ! escaped ;
j + + ;
}
if ( escaped ) {
j + + ;
continue ;
}
2015-02-20 00:34:04 +00:00
}
2022-06-03 16:47:37 +00:00
} while ( cc ! = quotation ) ;
} else if ( cc = = c ) {
stack + + ;
} else if ( cc = = closec ) {
stack - - ;
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( stack = = 0 ) {
brace_matching . write [ caret ] . open_match_line = i ;
brace_matching . write [ caret ] . open_match_column = j ;
brace_matching . write [ caret ] . open_matching = true ;
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
break ;
}
}
if ( brace_matching . write [ caret ] . open_match_line ! = - 1 ) {
2014-12-17 04:53:34 +00:00
break ;
}
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( ! brace_matching . write [ caret ] . open_matching ) {
brace_matching . write [ caret ] . open_mismatch = true ;
}
2020-05-14 14:41:43 +00:00
}
2014-12-17 04:53:34 +00:00
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( get_caret_column ( caret ) > 0 ) {
char32_t c = text [ get_caret_line ( caret ) ] [ get_caret_column ( caret ) - 1 ] ;
char32_t closec = 0 ;
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( c = = ' ] ' ) {
closec = ' [ ' ;
} else if ( c = = ' } ' ) {
closec = ' { ' ;
} else if ( c = = ' ) ' ) {
closec = ' ( ' ;
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( closec ! = 0 ) {
int stack = 1 ;
for ( int i = get_caret_line ( caret ) ; i > = 0 ; i - - ) {
int from = i = = get_caret_line ( caret ) ? get_caret_column ( caret ) - 2 : text [ i ] . length ( ) - 1 ;
for ( int j = from ; j > = 0 ; j - - ) {
char32_t cc = text [ i ] [ j ] ;
// Ignore any brackets inside a string.
if ( cc = = ' " ' | | cc = = ' \' ' ) {
char32_t quotation = cc ;
do {
j - - ;
if ( ! ( j > = 0 ) ) {
break ;
2015-02-20 00:34:04 +00:00
}
2022-06-03 16:47:37 +00:00
cc = text [ i ] [ j ] ;
// Skip over escaped quotation marks inside strings.
if ( cc = = quotation ) {
bool escaped = false ;
while ( j - 1 > = 0 & & text [ i ] [ j - 1 ] = = ' \\ ' ) {
escaped = ! escaped ;
j - - ;
}
if ( escaped ) {
cc = ' \\ ' ;
continue ;
}
2015-02-20 00:34:04 +00:00
}
2022-06-03 16:47:37 +00:00
} while ( cc ! = quotation ) ;
} else if ( cc = = c ) {
stack + + ;
} else if ( cc = = closec ) {
stack - - ;
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( stack = = 0 ) {
brace_matching . write [ caret ] . close_match_line = i ;
brace_matching . write [ caret ] . close_match_column = j ;
brace_matching . write [ caret ] . close_matching = true ;
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
break ;
}
}
if ( brace_matching . write [ caret ] . close_match_line ! = - 1 ) {
2014-12-17 04:53:34 +00:00
break ;
}
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( ! brace_matching . write [ caret ] . close_matching ) {
brace_matching . write [ caret ] . close_mismatch = true ;
}
2020-05-14 14:41:43 +00:00
}
2014-12-17 04:53:34 +00:00
}
}
}
2016-03-08 23:00:52 +00:00
2024-07-27 21:14:51 +00:00
bool draw_placeholder = _using_placeholder ( ) ;
2022-01-23 20:07:35 +00:00
2019-08-18 15:56:24 +00:00
// Get the highlighted words.
2022-06-03 16:47:37 +00:00
String highlighted_text = get_selected_text ( 0 ) ;
2016-03-16 21:20:42 +00:00
2021-03-12 13:35:16 +00:00
// Check if highlighted words contain only whitespaces (tabs or spaces).
2021-12-09 09:42:46 +00:00
bool only_whitespaces_highlighted = highlighted_text . strip_edges ( ) . is_empty ( ) ;
2019-04-27 16:02:09 +00:00
2022-06-03 16:47:37 +00:00
HashMap < int , HashSet < int > > caret_line_wrap_index_map ;
Vector < int > carets_wrap_index ;
carets_wrap_index . resize ( carets . size ( ) ) ;
for ( int i = 0 ; i < carets . size ( ) ; i + + ) {
carets . write [ i ] . visible = false ;
int wrap_index = get_caret_wrap_index ( i ) ;
caret_line_wrap_index_map [ get_caret_line ( i ) ] . insert ( wrap_index ) ;
carets_wrap_index . write [ i ] = wrap_index ;
}
2018-01-26 01:41:17 +00:00
2022-09-29 09:53:28 +00:00
int first_vis_line = get_first_visible_line ( ) - 1 ;
2018-01-26 01:41:17 +00:00
int draw_amount = visible_rows + ( smooth_scroll_enabled ? 1 : 0 ) ;
2022-09-29 09:53:28 +00:00
draw_amount + = draw_placeholder ? placeholder_wraped_rows . size ( ) - 1 : get_line_wrap_count ( first_vis_line + 1 ) ;
2019-08-11 18:31:19 +00:00
2021-11-18 15:55:43 +00:00
// Draw minimap.
2019-08-11 18:31:19 +00:00
if ( draw_minimap ) {
2021-07-09 16:27:09 +00:00
int minimap_visible_lines = get_minimap_visible_lines ( ) ;
2019-08-11 18:31:19 +00:00
int minimap_line_height = ( minimap_char_size . y + minimap_line_spacing ) ;
2024-05-08 14:29:12 +00:00
int tab_size = text . get_tab_size ( ) ;
2019-08-11 18:31:19 +00:00
2023-03-08 22:00:27 +00:00
// Calculate viewport size and y offset.
2019-08-11 18:31:19 +00:00
int viewport_height = ( draw_amount - 1 ) * minimap_line_height ;
2019-08-25 13:49:13 +00:00
int control_height = _get_control_height ( ) - viewport_height ;
2022-09-29 09:53:28 +00:00
int viewport_offset_y = round ( get_scroll_pos_for_line ( first_vis_line + 1 ) * control_height ) / ( ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? ( minimap_visible_lines - draw_amount ) : ( v_scroll - > get_max ( ) - draw_amount ) ) ;
2019-08-11 18:31:19 +00:00
2023-03-08 22:00:27 +00:00
// Calculate the first line.
2019-08-11 18:31:19 +00:00
int num_lines_before = round ( ( viewport_offset_y ) / minimap_line_height ) ;
2022-09-29 09:53:28 +00:00
int minimap_line = ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? - 1 : first_vis_line ;
2019-08-11 18:31:19 +00:00
if ( minimap_line > = 0 ) {
2022-09-29 09:53:28 +00:00
minimap_line - = get_next_visible_line_index_offset_from ( first_vis_line , 0 , - num_lines_before ) . x ;
2019-10-20 12:27:53 +00:00
minimap_line - = ( minimap_line > 0 & & smooth_scroll_enabled ? 1 : 0 ) ;
2019-08-11 18:31:19 +00:00
}
2021-07-09 10:52:49 +00:00
int minimap_draw_amount = minimap_visible_lines + get_line_wrap_count ( minimap_line + 1 ) ;
2019-08-11 18:31:19 +00:00
2020-02-02 18:47:33 +00:00
// Draw the minimap.
2024-02-23 19:49:13 +00:00
// Add visual feedback when dragging or hovering the visible area rectangle.
2024-04-25 06:20:14 +00:00
Color viewport_color = theme_cache . caret_color ;
2020-02-02 18:47:33 +00:00
if ( dragging_minimap ) {
2024-04-25 06:20:14 +00:00
viewport_color . a = 0.25 ;
2020-02-02 18:47:33 +00:00
} else if ( hovering_minimap ) {
2024-04-25 06:20:14 +00:00
viewport_color . a = 0.175 ;
2020-02-02 18:47:33 +00:00
} else {
2024-04-25 06:20:14 +00:00
viewport_color . a = 0.1 ;
2020-02-02 18:47:33 +00:00
}
2020-09-18 07:36:10 +00:00
if ( rtl ) {
2021-07-09 16:27:09 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( size . width - ( xmargin_end + 2 ) - minimap_width , viewport_offset_y , minimap_width , viewport_height ) , viewport_color ) ;
2020-09-18 07:36:10 +00:00
} else {
2021-07-09 16:27:09 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ( xmargin_end + 2 ) , viewport_offset_y , minimap_width , viewport_height ) , viewport_color ) ;
2020-09-18 07:36:10 +00:00
}
2020-02-02 18:47:33 +00:00
2019-08-11 18:31:19 +00:00
for ( int i = 0 ; i < minimap_draw_amount ; i + + ) {
minimap_line + + ;
if ( minimap_line < 0 | | minimap_line > = ( int ) text . size ( ) ) {
break ;
}
2021-07-09 12:07:10 +00:00
while ( _is_line_hidden ( minimap_line ) ) {
2019-08-11 18:31:19 +00:00
minimap_line + + ;
if ( minimap_line < 0 | | minimap_line > = ( int ) text . size ( ) ) {
break ;
}
}
2019-10-26 12:37:25 +00:00
if ( minimap_line < 0 | | minimap_line > = ( int ) text . size ( ) ) {
break ;
}
2020-05-03 16:08:15 +00:00
Dictionary color_map = _get_line_syntax_highlighting ( minimap_line ) ;
2019-08-11 18:31:19 +00:00
2020-09-10 20:25:00 +00:00
Color line_background_color = text . get_line_background_color ( minimap_line ) ;
2023-11-16 17:36:23 +00:00
if ( line_background_color ! = theme_cache . background_color ) {
// Make non-default background colors more visible, such as error markers.
line_background_color . a = 1.0 ;
} else {
line_background_color . a * = 0.6 ;
}
2023-04-03 16:01:10 +00:00
Color current_color = theme_cache . font_color ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2023-04-03 16:01:10 +00:00
current_color = theme_cache . font_readonly_color ;
2019-08-11 18:31:19 +00:00
}
2021-07-09 10:52:49 +00:00
Vector < String > wrap_rows = get_line_wrapped_text ( minimap_line ) ;
int line_wrap_amount = get_line_wrap_count ( minimap_line ) ;
2019-08-11 18:31:19 +00:00
int last_wrap_column = 0 ;
for ( int line_wrap_index = 0 ; line_wrap_index < line_wrap_amount + 1 ; line_wrap_index + + ) {
if ( line_wrap_index ! = 0 ) {
i + + ;
2020-05-14 14:41:43 +00:00
if ( i > = minimap_draw_amount ) {
2019-08-11 18:31:19 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2019-08-11 18:31:19 +00:00
}
const String & str = wrap_rows [ line_wrap_index ] ;
int indent_px = line_wrap_index ! = 0 ? get_indent_level ( minimap_line ) : 0 ;
2021-07-09 10:52:49 +00:00
if ( indent_px > = wrap_at_column ) {
2019-08-11 18:31:19 +00:00
indent_px = 0 ;
}
indent_px = minimap_char_size . x * indent_px ;
if ( line_wrap_index > 0 ) {
last_wrap_column + = wrap_rows [ line_wrap_index - 1 ] . length ( ) ;
}
2022-06-03 16:47:37 +00:00
if ( caret_line_wrap_index_map . has ( minimap_line ) & & caret_line_wrap_index_map [ minimap_line ] . has ( line_wrap_index ) & & highlight_current_line ) {
2020-09-18 07:36:10 +00:00
if ( rtl ) {
2023-04-03 16:01:10 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( size . width - ( xmargin_end + 2 ) - minimap_width , i * 3 , minimap_width , 2 ) , theme_cache . current_line_color ) ;
2020-09-18 07:36:10 +00:00
} else {
2023-04-03 16:01:10 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ( xmargin_end + 2 ) , i * 3 , minimap_width , 2 ) , theme_cache . current_line_color ) ;
2020-09-18 07:36:10 +00:00
}
2020-09-10 20:25:00 +00:00
} else if ( line_background_color ! = Color ( 0 , 0 , 0 , 0 ) ) {
if ( rtl ) {
2021-07-09 16:27:09 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( size . width - ( xmargin_end + 2 ) - minimap_width , i * 3 , minimap_width , 2 ) , line_background_color ) ;
2020-09-10 20:25:00 +00:00
} else {
2021-07-09 16:27:09 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ( xmargin_end + 2 ) , i * 3 , minimap_width , 2 ) , line_background_color ) ;
2020-09-10 20:25:00 +00:00
}
2019-08-11 18:31:19 +00:00
}
2024-05-08 14:29:12 +00:00
Color next_color = current_color ;
2019-08-11 18:31:19 +00:00
int characters = 0 ;
2024-05-08 14:29:12 +00:00
int tab_alignment = 0 ;
int xpos = xmargin_end + 2 + indent_px ;
2019-08-11 18:31:19 +00:00
for ( int j = 0 ; j < str . length ( ) ; j + + ) {
2024-05-08 14:29:12 +00:00
bool next_is_whitespace = false ;
bool next_is_tab = false ;
// Get the number of characters to draw together.
for ( characters = 0 ; j + characters < str . length ( ) ; characters + + ) {
int next_char_index = j + characters ;
const Variant * color_data = color_map . getptr ( last_wrap_column + next_char_index ) ;
if ( color_data ! = nullptr ) {
next_color = ( color_data - > operator Dictionary ( ) ) . get ( " color " , theme_cache . font_color ) ;
if ( ! editable ) {
next_color . a = theme_cache . font_readonly_color . a ;
}
next_color . a * = 0.6 ;
2019-08-11 18:31:19 +00:00
}
2024-05-08 14:29:12 +00:00
if ( characters = = 0 ) {
current_color = next_color ;
2019-08-11 18:31:19 +00:00
}
2024-05-08 14:29:12 +00:00
if ( next_color ! = current_color ) {
break ;
}
next_is_whitespace = is_whitespace ( str [ next_char_index ] ) ;
if ( next_is_whitespace ) {
if ( str [ next_char_index ] = = ' \t ' ) {
next_is_tab = true ;
2019-08-11 18:31:19 +00:00
}
2024-05-08 14:29:12 +00:00
break ;
}
bool out_of_bounds = xpos + minimap_char_size . x * characters > = xmargin_end + minimap_width ;
if ( out_of_bounds ) {
break ;
2019-08-11 18:31:19 +00:00
}
}
2024-05-08 14:29:12 +00:00
if ( ! next_is_whitespace & & characters = = 0 ) {
break ;
}
2019-08-11 18:31:19 +00:00
if ( characters > 0 ) {
2020-09-18 07:36:10 +00:00
if ( rtl ) {
2024-05-08 14:29:12 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( size . width - xpos - minimap_char_size . x * characters , minimap_line_height * i ) , Point2 ( minimap_char_size . x * characters , minimap_char_size . y ) ) , current_color ) ;
2020-09-18 07:36:10 +00:00
} else {
2024-05-08 14:29:12 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( xpos , minimap_line_height * i ) , Point2 ( minimap_char_size . x * characters , minimap_char_size . y ) ) , current_color ) ;
2020-09-18 07:36:10 +00:00
}
2019-08-11 18:31:19 +00:00
}
2024-05-08 14:29:12 +00:00
j + = characters - 1 ;
xpos + = minimap_char_size . x * characters ;
tab_alignment + = characters ;
2019-08-11 18:31:19 +00:00
2024-05-08 14:29:12 +00:00
if ( next_is_whitespace ) {
if ( next_is_tab ) {
tab_alignment % = tab_size ;
xpos + = minimap_char_size . x * ( tab_size - tab_alignment ) ;
tab_alignment = 0 ;
} else {
xpos + = minimap_char_size . x ;
tab_alignment + = 1 ;
}
j + = 1 ;
2019-08-11 18:31:19 +00:00
}
2024-05-08 14:29:12 +00:00
if ( xpos > = xmargin_end + minimap_width ) {
// Out of bounds.
break ;
}
2019-08-11 18:31:19 +00:00
}
}
}
}
2021-02-10 00:46:58 +00:00
int top_limit_y = 0 ;
int bottom_limit_y = get_size ( ) . height ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2023-04-03 16:01:10 +00:00
top_limit_y + = theme_cache . style_readonly - > get_margin ( SIDE_TOP ) ;
bottom_limit_y - = theme_cache . style_readonly - > get_margin ( SIDE_BOTTOM ) ;
2021-02-10 00:46:58 +00:00
} else {
2023-04-03 16:01:10 +00:00
top_limit_y + = theme_cache . style_normal - > get_margin ( SIDE_TOP ) ;
bottom_limit_y - = theme_cache . style_normal - > get_margin ( SIDE_BOTTOM ) ;
2021-02-10 00:46:58 +00:00
}
2021-11-18 15:55:43 +00:00
// Draw main text.
line_drawing_cache . clear ( ) ;
2023-04-03 16:01:10 +00:00
int row_height = draw_placeholder ? placeholder_line_height + theme_cache . line_spacing : get_line_height ( ) ;
2022-09-29 09:53:28 +00:00
int line = first_vis_line ;
2017-11-21 14:34:04 +00:00
for ( int i = 0 ; i < draw_amount ; i + + ) {
2017-11-12 23:12:17 +00:00
line + + ;
2020-05-14 14:41:43 +00:00
if ( line < 0 | | line > = ( int ) text . size ( ) ) {
2017-11-12 23:12:17 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-11-12 23:12:17 +00:00
2021-07-09 12:07:10 +00:00
while ( _is_line_hidden ( line ) ) {
2017-11-12 23:12:17 +00:00
line + + ;
if ( line < 0 | | line > = ( int ) text . size ( ) ) {
break ;
}
}
2016-03-08 23:00:52 +00:00
2020-05-14 14:41:43 +00:00
if ( line < 0 | | line > = ( int ) text . size ( ) ) {
2014-12-17 01:31:57 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2021-11-18 15:55:43 +00:00
LineDrawingCache cache_entry ;
2020-05-03 16:08:15 +00:00
Dictionary color_map = _get_line_syntax_highlighting ( line ) ;
2019-08-18 15:56:24 +00:00
// Ensure we at least use the font color.
2023-04-03 16:01:10 +00:00
Color current_color = ! editable ? theme_cache . font_readonly_color : theme_cache . font_color ;
2022-01-23 20:07:35 +00:00
if ( draw_placeholder ) {
2023-04-03 16:01:10 +00:00
current_color = theme_cache . font_placeholder_color ;
2022-01-23 20:07:35 +00:00
}
2017-08-19 14:23:45 +00:00
2022-01-23 20:07:35 +00:00
const Ref < TextParagraph > ldata = draw_placeholder ? placeholder_data_buf : text . get_line_data ( line ) ;
2016-03-08 23:00:52 +00:00
2022-01-23 20:07:35 +00:00
Vector < String > wrap_rows = draw_placeholder ? placeholder_wraped_rows : get_line_wrapped_text ( line ) ;
int line_wrap_amount = draw_placeholder ? placeholder_wraped_rows . size ( ) - 1 : get_line_wrap_count ( line ) ;
2016-05-28 16:25:45 +00:00
2020-09-18 07:36:10 +00:00
for ( int line_wrap_index = 0 ; line_wrap_index < = line_wrap_amount ; line_wrap_index + + ) {
2018-01-26 01:41:17 +00:00
if ( line_wrap_index ! = 0 ) {
i + + ;
2020-05-14 14:41:43 +00:00
if ( i > = draw_amount ) {
2018-01-26 01:41:17 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2018-01-26 01:41:17 +00:00
}
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
const String & str = wrap_rows [ line_wrap_index ] ;
2022-04-29 15:23:32 +00:00
int char_margin = xmargin_beg - first_visible_col ;
2018-01-26 01:41:17 +00:00
int ofs_x = 0 ;
2021-02-10 00:46:58 +00:00
int ofs_y = 0 ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2023-04-03 16:01:10 +00:00
ofs_x = theme_cache . style_readonly - > get_offset ( ) . x / 2 ;
ofs_x - = theme_cache . style_normal - > get_offset ( ) . x / 2 ;
ofs_y = theme_cache . style_readonly - > get_offset ( ) . y / 2 ;
2021-02-10 00:46:58 +00:00
} else {
2023-04-03 16:01:10 +00:00
ofs_y = theme_cache . style_normal - > get_offset ( ) . y / 2 ;
2018-04-02 11:41:44 +00:00
}
2016-03-08 23:00:52 +00:00
2023-04-03 16:01:10 +00:00
ofs_y + = i * row_height + theme_cache . line_spacing / 2 ;
2022-04-29 15:23:32 +00:00
ofs_y - = first_visible_line_wrap_ofs * row_height ;
2021-07-09 16:27:09 +00:00
ofs_y - = _get_v_scroll_offset ( ) * row_height ;
2017-10-21 20:35:50 +00:00
2021-02-10 00:46:58 +00:00
bool clipped = false ;
if ( ofs_y + row_height < top_limit_y ) {
// Line is outside the top margin, clip current line.
// Still need to go through the process to prepare color changes for next lines.
clipped = true ;
}
if ( ofs_y > bottom_limit_y ) {
// Line is outside the bottom margin, clip any remaining text.
i = draw_amount ;
break ;
}
2020-09-10 20:25:00 +00:00
if ( text . get_line_background_color ( line ) ! = Color ( 0 , 0 , 0 , 0 ) ) {
2020-09-18 07:36:10 +00:00
if ( rtl ) {
2020-09-10 20:25:00 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( size . width - ofs_x - xmargin_end , ofs_y , xmargin_end - xmargin_beg , row_height ) , text . get_line_background_color ( line ) ) ;
2020-09-18 07:36:10 +00:00
} else {
2020-09-10 20:25:00 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , xmargin_end - xmargin_beg , row_height ) , text . get_line_background_color ( line ) ) ;
2020-09-18 07:36:10 +00:00
}
2017-10-21 20:35:50 +00:00
}
2018-01-26 01:41:17 +00:00
2024-05-24 22:06:46 +00:00
// Draw current line highlight.
if ( highlight_current_line & & caret_line_wrap_index_map . has ( line ) & & caret_line_wrap_index_map [ line ] . has ( line_wrap_index ) ) {
if ( rtl ) {
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( size . width - ofs_x - xmargin_end , ofs_y , xmargin_end , row_height ) , theme_cache . current_line_color ) ;
} else {
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ofs_x , ofs_y , xmargin_end , row_height ) , theme_cache . current_line_color ) ;
2018-01-26 01:41:17 +00:00
}
2017-12-04 21:48:20 +00:00
}
2017-10-21 20:35:50 +00:00
2018-01-26 01:41:17 +00:00
if ( line_wrap_index = = 0 ) {
2019-08-18 15:56:24 +00:00
// Only do these if we are on the first wrapped part of a line.
2018-01-26 01:41:17 +00:00
2021-11-18 15:55:43 +00:00
cache_entry . y_offset = ofs_y ;
2023-04-03 16:01:10 +00:00
int gutter_offset = theme_cache . style_normal - > get_margin ( SIDE_LEFT ) ;
2020-07-25 00:15:23 +00:00
for ( int g = 0 ; g < gutters . size ( ) ; g + + ) {
const GutterInfo gutter = gutters [ g ] ;
if ( ! gutter . draw | | gutter . width < = 0 ) {
continue ;
}
switch ( gutter . type ) {
case GUTTER_TYPE_STRING : {
2022-09-29 09:53:28 +00:00
const String & txt = get_line_gutter_text ( line , g ) ;
if ( txt . is_empty ( ) ) {
2020-07-25 00:15:23 +00:00
break ;
}
2020-09-18 07:36:10 +00:00
Ref < TextLine > tl ;
2021-06-17 22:03:09 +00:00
tl . instantiate ( ) ;
2023-04-03 16:01:10 +00:00
tl - > add_string ( txt , theme_cache . font , theme_cache . font_size ) ;
2020-09-18 07:36:10 +00:00
int yofs = ofs_y + ( row_height - tl - > get_size ( ) . y ) / 2 ;
2023-04-03 16:01:10 +00:00
if ( theme_cache . outline_size > 0 & & theme_cache . outline_color . a > 0 ) {
tl - > draw_outline ( ci , Point2 ( gutter_offset + ofs_x , yofs ) , theme_cache . outline_size , theme_cache . outline_color ) ;
2020-12-25 21:45:28 +00:00
}
2020-09-18 07:36:10 +00:00
tl - > draw ( ci , Point2 ( gutter_offset + ofs_x , yofs ) , get_line_gutter_item_color ( line , g ) ) ;
2020-07-25 00:15:23 +00:00
} break ;
2021-07-19 08:16:00 +00:00
case GUTTER_TYPE_ICON : {
2020-07-25 00:15:23 +00:00
const Ref < Texture2D > icon = get_line_gutter_icon ( line , g ) ;
if ( icon . is_null ( ) ) {
break ;
}
2020-09-18 07:36:10 +00:00
Rect2 gutter_rect = Rect2 ( Point2i ( gutter_offset , ofs_y ) , Size2i ( gutter . width , row_height ) ) ;
2020-07-25 00:15:23 +00:00
int horizontal_padding = gutter_rect . size . x / 6 ;
int vertical_padding = gutter_rect . size . y / 6 ;
gutter_rect . position + = Point2 ( horizontal_padding , vertical_padding ) ;
gutter_rect . size - = Point2 ( horizontal_padding , vertical_padding ) * 2 ;
2020-09-18 07:36:10 +00:00
// Correct icon aspect ratio.
float icon_ratio = icon - > get_width ( ) / icon - > get_height ( ) ;
float gutter_ratio = gutter_rect . size . x / gutter_rect . size . y ;
if ( gutter_ratio > icon_ratio ) {
gutter_rect . size . x = floor ( icon - > get_width ( ) * ( gutter_rect . size . y / icon - > get_height ( ) ) ) ;
} else {
gutter_rect . size . y = floor ( icon - > get_height ( ) * ( gutter_rect . size . x / icon - > get_width ( ) ) ) ;
}
if ( rtl ) {
gutter_rect . position . x = size . width - gutter_rect . position . x - gutter_rect . size . x ;
}
2020-07-25 00:15:23 +00:00
icon - > draw_rect ( ci , gutter_rect , false , get_line_gutter_item_color ( line , g ) ) ;
} break ;
2021-07-19 08:16:00 +00:00
case GUTTER_TYPE_CUSTOM : {
2021-11-07 17:26:15 +00:00
if ( gutter . custom_draw_callback . is_valid ( ) ) {
Rect2i gutter_rect = Rect2i ( Point2i ( gutter_offset , ofs_y ) , Size2i ( gutter . width , row_height ) ) ;
if ( rtl ) {
gutter_rect . position . x = size . width - gutter_rect . position . x - gutter_rect . size . x ;
2020-07-25 00:15:23 +00:00
}
2023-07-11 14:18:10 +00:00
gutter . custom_draw_callback . call ( line , g , Rect2 ( gutter_rect ) ) ;
2020-07-25 00:15:23 +00:00
}
} break ;
}
gutter_offset + = gutter . width ;
}
2016-05-30 15:28:31 +00:00
}
2016-03-08 23:00:52 +00:00
2020-09-18 07:36:10 +00:00
// Draw line.
RID rid = ldata - > get_line_rid ( line_wrap_index ) ;
2022-05-09 09:47:10 +00:00
float text_height = TS - > shaped_text_get_size ( rid ) . y ;
2024-02-19 11:41:12 +00:00
float wrap_indent = ( text . is_indent_wrapped_lines ( ) & & line_wrap_index > 0 ) ? get_indent_level ( line ) * theme_cache . font - > get_char_size ( ' ' , theme_cache . font_size ) . width : 0.0 ;
2016-04-02 19:46:42 +00:00
2020-09-18 07:36:10 +00:00
if ( rtl ) {
2024-02-19 11:41:12 +00:00
char_margin = size . width - char_margin - ( TS - > shaped_text_get_size ( rid ) . x + wrap_indent ) ;
} else {
char_margin + = wrap_indent ;
2020-09-18 07:36:10 +00:00
}
2016-04-05 14:50:54 +00:00
2023-01-29 15:36:09 +00:00
// Draw selections.
float char_w = theme_cache . font - > get_char_size ( ' ' , theme_cache . font_size ) . width ;
2024-01-22 23:26:16 +00:00
for ( int c = 0 ; c < get_caret_count ( ) ; c + + ) {
2023-01-29 15:36:09 +00:00
if ( ! clipped & & has_selection ( c ) & & line > = get_selection_from_line ( c ) & & line < = get_selection_to_line ( c ) ) {
2022-06-03 16:47:37 +00:00
int sel_from = ( line > get_selection_from_line ( c ) ) ? TS - > shaped_text_get_range ( rid ) . x : get_selection_from_column ( c ) ;
int sel_to = ( line < get_selection_to_line ( c ) ) ? TS - > shaped_text_get_range ( rid ) . y : get_selection_to_column ( c ) ;
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , sel_from , sel_to ) ;
2023-01-29 15:36:09 +00:00
// Show selection at the end of line.
2024-03-17 22:28:28 +00:00
if ( line_wrap_index = = line_wrap_amount & & line < get_selection_to_line ( c ) ) {
2023-01-29 15:36:09 +00:00
if ( rtl ) {
sel . push_back ( Vector2 ( - char_w , 0 ) ) ;
} else {
float line_end = TS - > shaped_text_get_size ( rid ) . width ;
sel . push_back ( Vector2 ( line_end , line_end + char_w ) ) ;
}
}
2022-06-03 16:47:37 +00:00
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
2024-03-17 22:28:28 +00:00
Rect2 rect = Rect2 ( sel [ j ] . x + char_margin + ofs_x , ofs_y , Math : : ceil ( sel [ j ] . y ) - sel [ j ] . x , row_height ) ;
2022-06-03 16:47:37 +00:00
if ( rect . position . x + rect . size . x < = xmargin_beg | | rect . position . x > xmargin_end ) {
continue ;
}
if ( rect . position . x < xmargin_beg ) {
rect . size . x - = ( xmargin_beg - rect . position . x ) ;
rect . position . x = xmargin_beg ;
}
if ( rect . position . x + rect . size . x > xmargin_end ) {
rect . size . x = xmargin_end - rect . position . x ;
}
2023-04-03 16:01:10 +00:00
draw_rect ( rect , theme_cache . selection_color , true ) ;
2020-09-18 07:36:10 +00:00
}
2018-01-26 01:41:17 +00:00
}
2020-09-18 07:36:10 +00:00
}
2016-03-08 23:00:52 +00:00
2020-09-18 07:36:10 +00:00
int start = TS - > shaped_text_get_range ( rid ) . x ;
2023-03-08 22:00:27 +00:00
if ( ! clipped & & ! search_text . is_empty ( ) ) { // Search highlight
2020-09-18 07:36:10 +00:00
int search_text_col = _get_column_pos_of_word ( search_text , str , search_flags , 0 ) ;
2022-12-06 09:01:31 +00:00
int search_text_len = search_text . length ( ) ;
2020-09-18 07:36:10 +00:00
while ( search_text_col ! = - 1 ) {
2022-12-06 09:01:31 +00:00
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , search_text_col + start , search_text_col + search_text_len + start ) ;
2020-09-18 07:36:10 +00:00
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
Rect2 rect = Rect2 ( sel [ j ] . x + char_margin + ofs_x , ofs_y , sel [ j ] . y - sel [ j ] . x , row_height ) ;
if ( rect . position . x + rect . size . x < = xmargin_beg | | rect . position . x > xmargin_end ) {
continue ;
}
if ( rect . position . x < xmargin_beg ) {
rect . size . x - = ( xmargin_beg - rect . position . x ) ;
rect . position . x = xmargin_beg ;
} else if ( rect . position . x + rect . size . x > xmargin_end ) {
rect . size . x = xmargin_end - rect . position . x ;
}
2023-04-03 16:01:10 +00:00
draw_rect ( rect , theme_cache . search_result_color , true ) ;
draw_rect ( rect , theme_cache . search_result_border_color , false ) ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2022-12-06 09:01:31 +00:00
search_text_col = _get_column_pos_of_word ( search_text , str , search_flags , search_text_col + search_text_len ) ;
2018-01-26 01:41:17 +00:00
}
2020-09-18 07:36:10 +00:00
}
2018-01-14 16:06:27 +00:00
2021-02-10 00:46:58 +00:00
if ( ! clipped & & highlight_all_occurrences & & ! only_whitespaces_highlighted & & ! highlighted_text . is_empty ( ) ) { // Highlight
2020-09-18 07:36:10 +00:00
int highlighted_text_col = _get_column_pos_of_word ( highlighted_text , str , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , 0 ) ;
2022-12-06 09:01:31 +00:00
int highlighted_text_len = highlighted_text . length ( ) ;
2020-09-18 07:36:10 +00:00
while ( highlighted_text_col ! = - 1 ) {
2022-12-06 09:01:31 +00:00
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , highlighted_text_col + start , highlighted_text_col + highlighted_text_len + start ) ;
2020-09-18 07:36:10 +00:00
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
Rect2 rect = Rect2 ( sel [ j ] . x + char_margin + ofs_x , ofs_y , sel [ j ] . y - sel [ j ] . x , row_height ) ;
if ( rect . position . x + rect . size . x < = xmargin_beg | | rect . position . x > xmargin_end ) {
continue ;
}
if ( rect . position . x < xmargin_beg ) {
rect . size . x - = ( xmargin_beg - rect . position . x ) ;
rect . position . x = xmargin_beg ;
} else if ( rect . position . x + rect . size . x > xmargin_end ) {
rect . size . x = xmargin_end - rect . position . x ;
}
2023-04-03 16:01:10 +00:00
draw_rect ( rect , theme_cache . word_highlighted_color ) ;
2018-01-14 16:06:27 +00:00
}
2016-03-08 23:00:52 +00:00
2022-12-06 09:01:31 +00:00
highlighted_text_col = _get_column_pos_of_word ( highlighted_text , str , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , highlighted_text_col + highlighted_text_len ) ;
2018-01-26 01:41:17 +00:00
}
2020-09-18 07:36:10 +00:00
}
2016-05-28 16:25:45 +00:00
2021-07-01 16:10:54 +00:00
if ( ! clipped & & lookup_symbol_word . length ( ) ! = 0 ) { // Highlight word
2024-04-20 09:36:41 +00:00
if ( is_ascii_alphabet_char ( lookup_symbol_word [ 0 ] ) | | lookup_symbol_word [ 0 ] = = ' _ ' | | lookup_symbol_word [ 0 ] = = ' . ' ) {
2024-05-08 14:29:12 +00:00
Color highlight_underline_color = ! editable ? theme_cache . font_readonly_color : theme_cache . font_color ;
2022-12-06 09:01:31 +00:00
int lookup_symbol_word_col = _get_column_pos_of_word ( lookup_symbol_word , str , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , 0 ) ;
int lookup_symbol_word_len = lookup_symbol_word . length ( ) ;
while ( lookup_symbol_word_col ! = - 1 ) {
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , lookup_symbol_word_col + start , lookup_symbol_word_col + lookup_symbol_word_len + start ) ;
2020-09-18 07:36:10 +00:00
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
2023-04-03 16:01:10 +00:00
Rect2 rect = Rect2 ( sel [ j ] . x + char_margin + ofs_x , ofs_y + ( theme_cache . line_spacing / 2 ) , sel [ j ] . y - sel [ j ] . x , row_height ) ;
2020-09-18 07:36:10 +00:00
if ( rect . position . x + rect . size . x < = xmargin_beg | | rect . position . x > xmargin_end ) {
continue ;
}
if ( rect . position . x < xmargin_beg ) {
rect . size . x - = ( xmargin_beg - rect . position . x ) ;
rect . position . x = xmargin_beg ;
} else if ( rect . position . x + rect . size . x > xmargin_end ) {
rect . size . x = xmargin_end - rect . position . x ;
}
2023-04-03 16:01:10 +00:00
rect . position . y + = ceil ( TS - > shaped_text_get_ascent ( rid ) ) + ceil ( theme_cache . font - > get_underline_position ( theme_cache . font_size ) ) ;
rect . size . y = MAX ( 1 , theme_cache . font - > get_underline_thickness ( theme_cache . font_size ) ) ;
2024-05-08 14:29:12 +00:00
draw_rect ( rect , highlight_underline_color ) ;
2020-09-18 07:36:10 +00:00
}
2016-05-28 16:25:45 +00:00
2022-12-06 09:01:31 +00:00
lookup_symbol_word_col = _get_column_pos_of_word ( lookup_symbol_word , str , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , lookup_symbol_word_col + lookup_symbol_word_len ) ;
2020-05-14 14:41:43 +00:00
}
2016-05-28 16:25:45 +00:00
}
2020-09-18 07:36:10 +00:00
}
2016-05-28 16:25:45 +00:00
2020-09-18 07:36:10 +00:00
ofs_y + = ( row_height - text_height ) / 2 ;
2018-01-26 01:41:17 +00:00
2021-08-27 21:19:51 +00:00
const Glyph * glyphs = TS - > shaped_text_get_glyphs ( rid ) ;
int gl_size = TS - > shaped_text_get_glyph_count ( rid ) ;
2020-12-01 13:03:31 +00:00
2020-09-18 07:36:10 +00:00
ofs_y + = ldata - > get_line_ascent ( line_wrap_index ) ;
2021-11-18 15:55:43 +00:00
int first_visible_char = TS - > shaped_text_get_range ( rid ) . y ;
int last_visible_char = TS - > shaped_text_get_range ( rid ) . x ;
2022-01-09 17:05:43 +00:00
float char_ofs = 0 ;
2023-04-03 16:01:10 +00:00
if ( theme_cache . outline_size > 0 & & theme_cache . outline_color . a > 0 ) {
2020-12-25 21:45:28 +00:00
for ( int j = 0 ; j < gl_size ; j + + ) {
for ( int k = 0 ; k < glyphs [ j ] . repeat ; k + + ) {
if ( ( char_ofs + char_margin ) > = xmargin_beg & & ( char_ofs + glyphs [ j ] . advance + char_margin ) < = xmargin_end ) {
if ( glyphs [ j ] . font_rid ! = RID ( ) ) {
2023-04-03 16:01:10 +00:00
TS - > font_draw_glyph_outline ( glyphs [ j ] . font_rid , ci , glyphs [ j ] . font_size , theme_cache . outline_size , Vector2 ( char_margin + char_ofs + ofs_x + glyphs [ j ] . x_off , ofs_y + glyphs [ j ] . y_off ) , glyphs [ j ] . index , theme_cache . outline_color ) ;
2020-12-25 21:45:28 +00:00
}
}
char_ofs + = glyphs [ j ] . advance ;
}
if ( ( char_ofs + char_margin ) > = xmargin_end ) {
break ;
}
}
char_ofs = 0 ;
}
2020-12-01 13:03:31 +00:00
for ( int j = 0 ; j < gl_size ; j + + ) {
2023-01-18 07:33:35 +00:00
int64_t color_start = - 1 ;
for ( const Variant * key = color_map . next ( nullptr ) ; key ; key = color_map . next ( key ) ) {
if ( int64_t ( * key ) < = glyphs [ j ] . start ) {
color_start = * key ;
} else {
break ;
}
}
const Variant * color_data = ( color_start > = 0 ) ? color_map . getptr ( color_start ) : nullptr ;
2021-08-27 19:05:05 +00:00
if ( color_data ! = nullptr ) {
2023-04-03 16:01:10 +00:00
current_color = ( color_data - > operator Dictionary ( ) ) . get ( " color " , theme_cache . font_color ) ;
if ( ! editable & & current_color . a > theme_cache . font_readonly_color . a ) {
current_color . a = theme_cache . font_readonly_color . a ;
2018-01-26 01:41:17 +00:00
}
2017-10-21 20:35:50 +00:00
}
2022-09-23 07:20:13 +00:00
Color gl_color = current_color ;
2018-01-26 01:41:17 +00:00
2024-01-22 23:26:16 +00:00
for ( int c = 0 ; c < get_caret_count ( ) ; c + + ) {
2022-06-03 16:47:37 +00:00
if ( has_selection ( c ) & & line > = get_selection_from_line ( c ) & & line < = get_selection_to_line ( c ) ) { // Selection
int sel_from = ( line > get_selection_from_line ( c ) ) ? TS - > shaped_text_get_range ( rid ) . x : get_selection_from_column ( c ) ;
int sel_to = ( line < get_selection_to_line ( c ) ) ? TS - > shaped_text_get_range ( rid ) . y : get_selection_to_column ( c ) ;
2020-09-18 07:36:10 +00:00
2022-10-25 22:46:06 +00:00
if ( glyphs [ j ] . start > = sel_from & & glyphs [ j ] . end < = sel_to & & use_selected_font_color ) {
2023-04-03 16:01:10 +00:00
gl_color = theme_cache . font_selected_color ;
2022-06-03 16:47:37 +00:00
}
2018-01-26 01:41:17 +00:00
}
2017-10-21 20:35:50 +00:00
}
2017-09-28 14:10:30 +00:00
2022-01-09 17:05:43 +00:00
float char_pos = char_ofs + char_margin + ofs_x ;
2021-01-14 22:19:00 +00:00
if ( char_pos > = xmargin_beg ) {
2021-06-30 10:58:13 +00:00
if ( highlight_matching_braces_enabled ) {
2024-01-22 23:26:16 +00:00
for ( int c = 0 ; c < get_caret_count ( ) ; c + + ) {
2022-06-03 16:47:37 +00:00
if ( ( brace_matching [ c ] . open_match_line = = line & & brace_matching [ c ] . open_match_column = = glyphs [ j ] . start ) | |
( get_caret_column ( c ) = = glyphs [ j ] . start & & get_caret_line ( c ) = = line & & carets_wrap_index [ c ] = = line_wrap_index & & ( brace_matching [ c ] . open_matching | | brace_matching [ c ] . open_mismatch ) ) ) {
if ( brace_matching [ c ] . open_mismatch ) {
2023-09-12 13:01:42 +00:00
gl_color = _get_brace_mismatch_color ( ) ;
2022-06-03 16:47:37 +00:00
}
2023-04-03 16:01:10 +00:00
Rect2 rect = Rect2 ( char_pos , ofs_y + theme_cache . font - > get_underline_position ( theme_cache . font_size ) , glyphs [ j ] . advance * glyphs [ j ] . repeat , MAX ( theme_cache . font - > get_underline_thickness ( theme_cache . font_size ) * theme_cache . base_scale , 1 ) ) ;
2022-06-03 16:47:37 +00:00
draw_rect ( rect , gl_color ) ;
2021-01-14 22:19:00 +00:00
}
2016-05-28 16:25:45 +00:00
2022-06-03 16:47:37 +00:00
if ( ( brace_matching [ c ] . close_match_line = = line & & brace_matching [ c ] . close_match_column = = glyphs [ j ] . start ) | |
( get_caret_column ( c ) = = glyphs [ j ] . start + 1 & & get_caret_line ( c ) = = line & & carets_wrap_index [ c ] = = line_wrap_index & & ( brace_matching [ c ] . close_matching | | brace_matching [ c ] . close_mismatch ) ) ) {
if ( brace_matching [ c ] . close_mismatch ) {
2023-09-12 13:01:42 +00:00
gl_color = _get_brace_mismatch_color ( ) ;
2022-06-03 16:47:37 +00:00
}
2023-04-03 16:01:10 +00:00
Rect2 rect = Rect2 ( char_pos , ofs_y + theme_cache . font - > get_underline_position ( theme_cache . font_size ) , glyphs [ j ] . advance * glyphs [ j ] . repeat , MAX ( theme_cache . font - > get_underline_thickness ( theme_cache . font_size ) * theme_cache . base_scale , 1 ) ) ;
2022-06-03 16:47:37 +00:00
draw_rect ( rect , gl_color ) ;
2021-01-14 22:19:00 +00:00
}
2020-05-14 14:41:43 +00:00
}
2021-01-14 22:19:00 +00:00
}
if ( draw_tabs & & ( ( glyphs [ j ] . flags & TextServer : : GRAPHEME_IS_TAB ) = = TextServer : : GRAPHEME_IS_TAB ) ) {
2023-04-03 16:01:10 +00:00
int yofs = ( text_height - theme_cache . tab_icon - > get_height ( ) ) / 2 - ldata - > get_line_ascent ( line_wrap_index ) ;
theme_cache . tab_icon - > draw ( ci , Point2 ( char_pos , ofs_y + yofs ) , gl_color ) ;
2023-03-06 12:59:50 +00:00
} else if ( draw_spaces & & ( ( glyphs [ j ] . flags & TextServer : : GRAPHEME_IS_SPACE ) = = TextServer : : GRAPHEME_IS_SPACE ) & & ( ( glyphs [ j ] . flags & TextServer : : GRAPHEME_IS_VIRTUAL ) ! = TextServer : : GRAPHEME_IS_VIRTUAL ) ) {
2023-04-03 16:01:10 +00:00
int yofs = ( text_height - theme_cache . space_icon - > get_height ( ) ) / 2 - ldata - > get_line_ascent ( line_wrap_index ) ;
int xofs = ( glyphs [ j ] . advance * glyphs [ j ] . repeat - theme_cache . space_icon - > get_width ( ) ) / 2 ;
theme_cache . space_icon - > draw ( ci , Point2 ( char_pos + xofs , ofs_y + yofs ) , gl_color ) ;
2018-01-26 01:41:17 +00:00
}
}
2021-01-14 22:19:00 +00:00
2021-11-18 15:55:43 +00:00
bool had_glyphs_drawn = false ;
2020-09-18 07:36:10 +00:00
for ( int k = 0 ; k < glyphs [ j ] . repeat ; k + + ) {
2021-02-10 00:46:58 +00:00
if ( ! clipped & & ( char_ofs + char_margin ) > = xmargin_beg & & ( char_ofs + glyphs [ j ] . advance + char_margin ) < = xmargin_end ) {
2020-09-18 07:36:10 +00:00
if ( glyphs [ j ] . font_rid ! = RID ( ) ) {
2022-09-23 07:20:13 +00:00
TS - > font_draw_glyph ( glyphs [ j ] . font_rid , ci , glyphs [ j ] . font_size , Vector2 ( char_margin + char_ofs + ofs_x + glyphs [ j ] . x_off , ofs_y + glyphs [ j ] . y_off ) , glyphs [ j ] . index , gl_color ) ;
2021-11-18 15:55:43 +00:00
had_glyphs_drawn = true ;
2023-04-12 07:08:51 +00:00
} else if ( ( ( glyphs [ j ] . flags & TextServer : : GRAPHEME_IS_VIRTUAL ) ! = TextServer : : GRAPHEME_IS_VIRTUAL ) & & ( ( glyphs [ j ] . flags & TextServer : : GRAPHEME_IS_EMBEDDED_OBJECT ) ! = TextServer : : GRAPHEME_IS_EMBEDDED_OBJECT ) ) {
2022-09-23 07:20:13 +00:00
TS - > draw_hex_code_box ( ci , glyphs [ j ] . font_size , Vector2 ( char_margin + char_ofs + ofs_x + glyphs [ j ] . x_off , ofs_y + glyphs [ j ] . y_off ) , glyphs [ j ] . index , gl_color ) ;
2021-11-18 15:55:43 +00:00
had_glyphs_drawn = true ;
2020-09-18 07:36:10 +00:00
}
2016-03-16 21:20:42 +00:00
}
2020-09-18 07:36:10 +00:00
char_ofs + = glyphs [ j ] . advance ;
}
2021-11-18 15:55:43 +00:00
if ( had_glyphs_drawn ) {
if ( first_visible_char > glyphs [ j ] . start ) {
first_visible_char = glyphs [ j ] . start ;
2023-09-11 10:32:36 +00:00
}
if ( last_visible_char < glyphs [ j ] . end ) {
2021-11-18 15:55:43 +00:00
last_visible_char = glyphs [ j ] . end ;
}
}
2020-09-18 07:36:10 +00:00
if ( ( char_ofs + char_margin ) > = xmargin_end ) {
break ;
}
}
2016-03-16 21:20:42 +00:00
2021-11-18 15:55:43 +00:00
cache_entry . first_visible_chars . push_back ( first_visible_char ) ;
cache_entry . last_visible_chars . push_back ( last_visible_char ) ;
2021-03-13 14:09:49 +00:00
// is_line_folded
2021-07-09 12:07:10 +00:00
if ( line_wrap_index = = line_wrap_amount & & line < text . size ( ) - 1 & & _is_line_hidden ( line + 1 ) ) {
2023-09-12 13:01:42 +00:00
int xofs = char_ofs + char_margin + ofs_x + ( _get_folded_eol_icon ( ) - > get_width ( ) / 2 ) ;
2021-01-14 22:19:00 +00:00
if ( xofs > = xmargin_beg & & xofs < xmargin_end ) {
2023-09-12 13:01:42 +00:00
int yofs = ( text_height - _get_folded_eol_icon ( ) - > get_height ( ) ) / 2 - ldata - > get_line_ascent ( line_wrap_index ) ;
Color eol_color = _get_code_folding_color ( ) ;
2021-01-14 22:19:00 +00:00
eol_color . a = 1 ;
2023-09-12 13:01:42 +00:00
_get_folded_eol_icon ( ) - > draw ( ci , Point2 ( xofs , ofs_y + yofs ) , eol_color ) ;
2021-01-14 22:19:00 +00:00
}
2020-09-18 07:36:10 +00:00
}
2018-01-26 01:41:17 +00:00
2021-10-02 20:07:42 +00:00
// Carets.
2022-03-02 23:15:48 +00:00
// Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
2023-04-03 16:01:10 +00:00
const int caret_width = theme_cache . caret_width * MAX ( 1 , theme_cache . base_scale ) ;
2020-09-13 20:14:20 +00:00
2022-06-03 16:47:37 +00:00
for ( int c = 0 ; c < carets . size ( ) ; c + + ) {
if ( ! clipped & & get_caret_line ( c ) = = line & & carets_wrap_index [ c ] = = line_wrap_index ) {
carets . write [ c ] . draw_pos . y = ofs_y + ldata - > get_line_descent ( line_wrap_index ) ;
2021-01-11 19:59:56 +00:00
2022-12-10 23:21:22 +00:00
if ( ime_text . is_empty ( ) | | ime_selection . y = = 0 ) {
// Normal caret.
2022-06-03 16:47:37 +00:00
CaretInfo ts_caret ;
2022-12-10 23:21:22 +00:00
if ( ! str . is_empty ( ) | | ! ime_text . is_empty ( ) ) {
2022-06-03 16:47:37 +00:00
// Get carets.
2022-12-10 23:21:22 +00:00
ts_caret = TS - > shaped_text_get_carets ( rid , ime_text . is_empty ( ) ? get_caret_column ( c ) : get_caret_column ( c ) + ime_selection . x ) ;
2020-09-18 07:36:10 +00:00
} else {
2022-06-03 16:47:37 +00:00
// No carets, add one at the start.
2023-04-03 16:01:10 +00:00
int h = theme_cache . font - > get_height ( theme_cache . font_size ) ;
2022-06-03 16:47:37 +00:00
if ( rtl ) {
ts_caret . l_dir = TextServer : : DIRECTION_RTL ;
ts_caret . l_caret = Rect2 ( Vector2 ( xmargin_end - char_margin + ofs_x , - h / 2 ) , Size2 ( caret_width * 4 , h ) ) ;
} else {
ts_caret . l_dir = TextServer : : DIRECTION_LTR ;
ts_caret . l_caret = Rect2 ( Vector2 ( char_ofs , - h / 2 ) , Size2 ( caret_width * 4 , h ) ) ;
}
2018-01-26 01:41:17 +00:00
}
2018-08-02 07:38:56 +00:00
2022-06-03 16:47:37 +00:00
if ( ( ts_caret . l_caret ! = Rect2 ( ) & & ( ts_caret . l_dir = = TextServer : : DIRECTION_AUTO | | ts_caret . l_dir = = ( TextServer : : Direction ) input_direction ) ) | | ( ts_caret . t_caret = = Rect2 ( ) ) ) {
carets . write [ c ] . draw_pos . x = char_margin + ofs_x + ts_caret . l_caret . position . x ;
} else {
carets . write [ c ] . draw_pos . x = char_margin + ofs_x + ts_caret . t_caret . position . x ;
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
if ( get_caret_draw_pos ( c ) . x > = xmargin_beg & & get_caret_draw_pos ( c ) . x < xmargin_end ) {
carets . write [ c ] . visible = true ;
if ( draw_caret | | drag_caret_force_displayed ) {
if ( caret_type = = CaretType : : CARET_TYPE_BLOCK | | overtype_mode ) {
//Block or underline caret, draw trailing carets at full height.
2023-04-03 16:01:10 +00:00
int h = theme_cache . font - > get_height ( theme_cache . font_size ) ;
2020-09-13 20:14:20 +00:00
2022-06-03 16:47:37 +00:00
if ( ts_caret . t_caret ! = Rect2 ( ) ) {
2021-07-19 10:18:05 +00:00
if ( overtype_mode ) {
2022-06-03 16:47:37 +00:00
ts_caret . t_caret . position . y = TS - > shaped_text_get_descent ( rid ) ;
ts_caret . t_caret . size . y = caret_width ;
} else {
ts_caret . t_caret . position . y = - TS - > shaped_text_get_ascent ( rid ) ;
ts_caret . t_caret . size . y = h ;
}
ts_caret . t_caret . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
2023-04-03 16:01:10 +00:00
draw_rect ( ts_caret . t_caret , theme_cache . caret_color , overtype_mode ) ;
2022-06-03 16:47:37 +00:00
if ( ts_caret . l_caret ! = Rect2 ( ) & & ts_caret . l_dir ! = ts_caret . t_dir ) {
2022-10-06 11:30:50 +00:00
// Draw split caret (leading part).
2022-06-03 16:47:37 +00:00
ts_caret . l_caret . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
ts_caret . l_caret . size . x = caret_width ;
2023-04-03 16:01:10 +00:00
draw_rect ( ts_caret . l_caret , theme_cache . caret_color ) ;
2022-10-06 11:30:50 +00:00
// Draw extra direction marker on top of split caret.
float d = ( ts_caret . l_dir = = TextServer : : DIRECTION_LTR ) ? 0.5 : - 3 ;
Rect2 trect = Rect2 ( ts_caret . l_caret . position . x + d * caret_width , ts_caret . l_caret . position . y + ts_caret . l_caret . size . y - caret_width , 3 * caret_width , caret_width ) ;
2023-04-03 16:01:10 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , trect , theme_cache . caret_color ) ;
2022-06-03 16:47:37 +00:00
}
} else { // End of the line.
if ( gl_size > 0 ) {
// Adjust for actual line dimensions.
if ( overtype_mode ) {
ts_caret . l_caret . position . y = TS - > shaped_text_get_descent ( rid ) ;
ts_caret . l_caret . size . y = caret_width ;
} else {
ts_caret . l_caret . position . y = - TS - > shaped_text_get_ascent ( rid ) ;
ts_caret . l_caret . size . y = h ;
}
} else if ( overtype_mode ) {
ts_caret . l_caret . position . y + = ts_caret . l_caret . size . y ;
2021-08-27 21:19:51 +00:00
ts_caret . l_caret . size . y = caret_width ;
2022-06-03 16:47:37 +00:00
}
2023-04-02 17:53:00 +00:00
if ( Math : : ceil ( ts_caret . l_caret . position . x ) > = TS - > shaped_text_get_size ( rid ) . x ) {
2023-04-03 16:01:10 +00:00
ts_caret . l_caret . size . x = theme_cache . font - > get_char_size ( ' m ' , theme_cache . font_size ) . x ;
2021-07-19 10:18:05 +00:00
} else {
2022-06-03 16:47:37 +00:00
ts_caret . l_caret . size . x = 3 * caret_width ;
}
ts_caret . l_caret . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
if ( ts_caret . l_dir = = TextServer : : DIRECTION_RTL ) {
ts_caret . l_caret . position . x - = ts_caret . l_caret . size . x ;
2021-07-19 10:18:05 +00:00
}
2023-04-03 16:01:10 +00:00
draw_rect ( ts_caret . l_caret , theme_cache . caret_color , overtype_mode ) ;
2021-07-19 10:18:05 +00:00
}
2022-06-03 16:47:37 +00:00
} else {
// Normal caret.
if ( ts_caret . l_caret ! = Rect2 ( ) & & ts_caret . l_dir = = TextServer : : DIRECTION_AUTO ) {
// Draw extra marker on top of mid caret.
2022-10-06 11:30:50 +00:00
Rect2 trect = Rect2 ( ts_caret . l_caret . position . x - 2.5 * caret_width , ts_caret . l_caret . position . y , 6 * caret_width , caret_width ) ;
trect . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
2023-04-03 16:01:10 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , trect , theme_cache . caret_color ) ;
2022-10-06 11:30:50 +00:00
} else if ( ts_caret . l_caret ! = Rect2 ( ) & & ts_caret . t_caret ! = Rect2 ( ) & & ts_caret . l_dir ! = ts_caret . t_dir ) {
// Draw extra direction marker on top of split caret.
float d = ( ts_caret . l_dir = = TextServer : : DIRECTION_LTR ) ? 0.5 : - 3 ;
Rect2 trect = Rect2 ( ts_caret . l_caret . position . x + d * caret_width , ts_caret . l_caret . position . y + ts_caret . l_caret . size . y - caret_width , 3 * caret_width , caret_width ) ;
trect . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
2023-04-03 16:01:10 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , trect , theme_cache . caret_color ) ;
2022-10-06 11:30:50 +00:00
d = ( ts_caret . t_dir = = TextServer : : DIRECTION_LTR ) ? 0.5 : - 3 ;
trect = Rect2 ( ts_caret . t_caret . position . x + d * caret_width , ts_caret . t_caret . position . y , 3 * caret_width , caret_width ) ;
2022-06-03 16:47:37 +00:00
trect . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
2023-04-03 16:01:10 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , trect , theme_cache . caret_color ) ;
2020-09-13 20:14:20 +00:00
}
2021-08-27 21:19:51 +00:00
ts_caret . l_caret . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
2022-06-03 16:47:37 +00:00
ts_caret . l_caret . size . x = caret_width ;
2019-04-23 23:33:20 +00:00
2023-04-03 16:01:10 +00:00
draw_rect ( ts_caret . l_caret , theme_cache . caret_color ) ;
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
ts_caret . t_caret . position + = Vector2 ( char_margin + ofs_x , ofs_y ) ;
ts_caret . t_caret . size . x = caret_width ;
2016-05-09 18:21:55 +00:00
2023-04-03 16:01:10 +00:00
draw_rect ( ts_caret . t_caret , theme_cache . caret_color ) ;
2022-06-03 16:47:37 +00:00
}
2020-09-13 20:14:20 +00:00
}
2020-05-14 14:41:43 +00:00
}
2022-12-10 23:21:22 +00:00
}
if ( ! ime_text . is_empty ( ) ) {
2022-06-03 16:47:37 +00:00
{
// IME Intermediate text range.
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , get_caret_column ( c ) , get_caret_column ( c ) + ime_text . length ( ) ) ;
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
Rect2 rect = Rect2 ( sel [ j ] . x + char_margin + ofs_x , ofs_y , sel [ j ] . y - sel [ j ] . x , text_height ) ;
if ( rect . position . x + rect . size . x < = xmargin_beg | | rect . position . x > xmargin_end ) {
continue ;
}
if ( rect . position . x < xmargin_beg ) {
rect . size . x - = ( xmargin_beg - rect . position . x ) ;
rect . position . x = xmargin_beg ;
} else if ( rect . position . x + rect . size . x > xmargin_end ) {
rect . size . x = xmargin_end - rect . position . x ;
}
rect . size . y = caret_width ;
2023-04-03 16:01:10 +00:00
draw_rect ( rect , theme_cache . caret_color ) ;
2022-06-03 16:47:37 +00:00
carets . write [ c ] . draw_pos . x = rect . position . x ;
2020-09-18 07:36:10 +00:00
}
2017-08-07 11:09:56 +00:00
}
2022-06-03 16:47:37 +00:00
{
// IME caret.
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , get_caret_column ( c ) + ime_selection . x , get_caret_column ( c ) + ime_selection . x + ime_selection . y ) ;
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
Rect2 rect = Rect2 ( sel [ j ] . x + char_margin + ofs_x , ofs_y , sel [ j ] . y - sel [ j ] . x , text_height ) ;
if ( rect . position . x + rect . size . x < = xmargin_beg | | rect . position . x > xmargin_end ) {
continue ;
}
if ( rect . position . x < xmargin_beg ) {
rect . size . x - = ( xmargin_beg - rect . position . x ) ;
rect . position . x = xmargin_beg ;
} else if ( rect . position . x + rect . size . x > xmargin_end ) {
rect . size . x = xmargin_end - rect . position . x ;
}
rect . size . y = caret_width * 3 ;
2023-04-03 16:01:10 +00:00
draw_rect ( rect , theme_cache . caret_color ) ;
2022-06-03 16:47:37 +00:00
carets . write [ c ] . draw_pos . x = rect . position . x ;
2020-09-18 07:36:10 +00:00
}
2017-08-07 11:09:56 +00:00
}
2016-05-09 18:21:55 +00:00
}
}
2014-12-17 01:31:57 +00:00
}
}
2021-11-18 15:55:43 +00:00
2022-04-22 16:52:31 +00:00
if ( ! draw_placeholder ) {
2022-01-23 20:07:35 +00:00
line_drawing_cache [ line ] = cache_entry ;
}
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2017-06-25 15:50:45 +00:00
if ( has_focus ( ) ) {
2024-01-22 17:34:12 +00:00
_update_ime_window_position ( ) ;
2017-06-25 15:50:45 +00:00
}
2014-12-17 01:31:57 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2014-12-17 01:31:57 +00:00
case NOTIFICATION_FOCUS_ENTER : {
2019-09-04 11:06:15 +00:00
if ( caret_blink_enabled ) {
caret_blink_timer - > start ( ) ;
} else {
2016-06-20 19:29:58 +00:00
draw_caret = true ;
}
2017-06-25 15:50:45 +00:00
2024-01-22 17:34:12 +00:00
_update_ime_window_position ( ) ;
2017-06-25 15:50:45 +00:00
2020-08-09 09:06:36 +00:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_VIRTUAL_KEYBOARD ) & & virtual_keyboard_enabled ) {
2021-07-08 17:35:56 +00:00
int caret_start = - 1 ;
int caret_end = - 1 ;
2020-11-15 12:09:20 +00:00
2022-06-03 16:47:37 +00:00
if ( ! has_selection ( 0 ) ) {
String full_text = _base_get_text ( 0 , 0 , get_caret_line ( ) , get_caret_column ( ) ) ;
2020-11-15 12:09:20 +00:00
2021-07-08 17:35:56 +00:00
caret_start = full_text . length ( ) ;
2020-12-16 02:36:14 +00:00
} else {
2022-04-29 15:23:32 +00:00
String pre_text = _base_get_text ( 0 , 0 , get_selection_from_line ( ) , get_selection_from_column ( ) ) ;
2022-06-03 16:47:37 +00:00
String post_text = get_selected_text ( 0 ) ;
2020-12-16 02:36:14 +00:00
2021-07-08 17:35:56 +00:00
caret_start = pre_text . length ( ) ;
caret_end = caret_start + post_text . length ( ) ;
2020-11-15 12:09:20 +00:00
}
2022-07-07 18:20:10 +00:00
DisplayServer : : get_singleton ( ) - > virtual_keyboard_show ( get_text ( ) , get_global_rect ( ) , DisplayServer : : KEYBOARD_TYPE_MULTILINE , - 1 , caret_start , caret_end ) ;
2020-05-14 14:41:43 +00:00
}
2014-12-17 01:31:57 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2014-12-17 01:31:57 +00:00
case NOTIFICATION_FOCUS_EXIT : {
2019-09-04 11:06:15 +00:00
if ( caret_blink_enabled ) {
caret_blink_timer - > stop ( ) ;
}
2024-01-22 17:34:12 +00:00
apply_ime ( ) ;
2017-06-25 15:50:45 +00:00
2020-08-09 09:06:36 +00:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_VIRTUAL_KEYBOARD ) & & virtual_keyboard_enabled ) {
2020-03-03 13:36:29 +00:00
DisplayServer : : get_singleton ( ) - > virtual_keyboard_hide ( ) ;
2020-05-14 14:41:43 +00:00
}
2021-10-21 21:02:46 +00:00
2022-04-29 15:23:32 +00:00
if ( deselect_on_focus_loss_enabled & & ! selection_drag_attempt ) {
2021-10-21 21:02:46 +00:00
deselect ( ) ;
}
2014-12-17 01:31:57 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2018-11-23 12:07:48 +00:00
case MainLoop : : NOTIFICATION_OS_IME_UPDATE : {
2019-01-08 20:52:56 +00:00
if ( has_focus ( ) ) {
2024-01-22 23:26:16 +00:00
bool had_ime_text = has_ime_text ( ) ;
2020-03-03 13:36:29 +00:00
ime_text = DisplayServer : : get_singleton ( ) - > ime_get_text ( ) ;
ime_selection = DisplayServer : : get_singleton ( ) - > ime_get_selection ( ) ;
2020-09-18 07:36:10 +00:00
2024-01-22 23:26:16 +00:00
if ( ! had_ime_text & & has_ime_text ( ) ) {
_cancel_drag_and_drop_text ( ) ;
}
if ( has_ime_text ( ) & & has_selection ( ) ) {
2022-12-10 23:21:22 +00:00
delete_selection ( ) ;
}
2024-01-22 17:34:12 +00:00
_update_ime_text ( ) ;
adjust_viewport_to_caret ( 0 ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2019-01-08 20:52:56 +00:00
}
2018-11-23 12:07:48 +00:00
} break ;
2022-02-15 17:06:48 +00:00
case NOTIFICATION_DRAG_BEGIN : {
2024-01-22 23:26:16 +00:00
set_selection_mode ( SelectionMode : : SELECTION_MODE_NONE ) ;
2021-11-24 21:22:40 +00:00
drag_action = true ;
dragging_minimap = false ;
dragging_selection = false ;
can_drag_minimap = false ;
click_select_held - > stop ( ) ;
} break ;
2022-02-15 17:06:48 +00:00
case NOTIFICATION_DRAG_END : {
2024-07-04 23:29:15 +00:00
remove_drag_caret ( ) ;
if ( selection_drag_attempt & & is_drag_successful ( ) ) {
// Dropped elsewhere.
if ( is_editable ( ) & & ! Input : : get_singleton ( ) - > is_key_pressed ( Key : : CMD_OR_CTRL ) ) {
delete_selection ( ) ;
} else if ( deselect_on_focus_loss_enabled ) {
deselect ( ) ;
2024-01-22 23:26:16 +00:00
}
}
selection_drag_attempt = false ;
2021-11-24 21:22:40 +00:00
drag_action = false ;
drag_caret_force_displayed = false ;
} break ;
2024-01-22 23:26:16 +00:00
case NOTIFICATION_MOUSE_EXIT_SELF : {
if ( drag_caret_force_displayed ) {
drag_caret_force_displayed = false ;
queue_redraw ( ) ;
}
} break ;
2018-11-23 12:07:48 +00:00
}
2017-08-07 11:09:56 +00:00
}
2022-01-11 13:59:52 +00:00
void TextEdit : : unhandled_key_input ( const Ref < InputEvent > & p_event ) {
Ref < InputEventKey > k = p_event ;
if ( k . is_valid ( ) ) {
if ( ! k - > is_pressed ( ) ) {
return ;
}
// Handle Unicode (with modifiers active, process after shortcuts).
if ( has_focus ( ) & & editable & & ( k - > get_unicode ( ) > = 32 ) ) {
handle_unicode_input ( k - > get_unicode ( ) ) ;
accept_event ( ) ;
}
}
}
2021-11-29 14:24:02 +00:00
bool TextEdit : : alt_input ( const Ref < InputEvent > & p_gui_input ) {
Ref < InputEventKey > k = p_gui_input ;
if ( k . is_valid ( ) ) {
if ( ! k - > is_pressed ( ) ) {
if ( alt_start & & k - > get_keycode ( ) = = Key : : ALT ) {
alt_start = false ;
if ( ( alt_code > 0x31 & & alt_code < 0xd800 ) | | ( alt_code > 0xdfff & & alt_code < = 0x10ffff ) ) {
handle_unicode_input ( alt_code ) ;
}
return true ;
}
return false ;
}
if ( k - > is_alt_pressed ( ) ) {
if ( ! alt_start ) {
if ( k - > get_keycode ( ) = = Key : : KP_ADD ) {
alt_start = true ;
alt_code = 0 ;
return true ;
}
} else {
if ( k - > get_keycode ( ) > = Key : : KEY_0 & & k - > get_keycode ( ) < = Key : : KEY_9 ) {
alt_code = alt_code < < 4 ;
alt_code + = ( uint32_t ) ( k - > get_keycode ( ) - Key : : KEY_0 ) ;
}
if ( k - > get_keycode ( ) > = Key : : KP_0 & & k - > get_keycode ( ) < = Key : : KP_9 ) {
alt_code = alt_code < < 4 ;
alt_code + = ( uint32_t ) ( k - > get_keycode ( ) - Key : : KP_0 ) ;
}
if ( k - > get_keycode ( ) > = Key : : A & & k - > get_keycode ( ) < = Key : : F ) {
alt_code = alt_code < < 4 ;
alt_code + = ( uint32_t ) ( k - > get_keycode ( ) - Key : : A ) + 10 ;
}
return true ;
}
}
}
return false ;
}
2021-08-22 15:37:22 +00:00
void TextEdit : : gui_input ( const Ref < InputEvent > & p_gui_input ) {
2021-07-10 10:41:38 +00:00
ERR_FAIL_COND ( p_gui_input . is_null ( ) ) ;
2017-11-12 23:12:17 +00:00
2021-07-10 10:41:38 +00:00
double prev_v_scroll = v_scroll - > get_value ( ) ;
double prev_h_scroll = h_scroll - > get_value ( ) ;
2015-11-07 12:39:03 +00:00
2021-07-10 10:41:38 +00:00
Ref < InputEventMouseButton > mb = p_gui_input ;
2021-06-15 14:05:01 +00:00
2021-07-10 10:41:38 +00:00
if ( mb . is_valid ( ) ) {
Vector2i mpos = mb - > get_position ( ) ;
if ( is_layout_rtl ( ) ) {
mpos . x = get_size ( ) . x - mpos . x ;
}
2020-12-07 11:32:00 +00:00
2021-07-10 10:41:38 +00:00
if ( mb - > is_pressed ( ) ) {
2022-09-02 09:37:48 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_UP & & ! mb - > is_command_or_control_pressed ( ) ) {
2021-07-10 10:41:38 +00:00
if ( mb - > is_shift_pressed ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) - ( 100 * mb - > get_factor ( ) ) ) ;
} else if ( mb - > is_alt_pressed ( ) ) {
// Scroll 5 times as fast as normal (like in Visual Studio Code).
2023-02-17 13:50:46 +00:00
_scroll_up ( 15 * mb - > get_factor ( ) , true ) ;
2021-07-10 10:41:38 +00:00
} else if ( v_scroll - > is_visible ( ) ) {
// Scroll 3 lines.
2023-02-17 13:50:46 +00:00
_scroll_up ( 3 * mb - > get_factor ( ) , true ) ;
2020-05-14 14:41:43 +00:00
}
2015-01-02 18:08:40 +00:00
}
2022-09-02 09:37:48 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_DOWN & & ! mb - > is_command_or_control_pressed ( ) ) {
2021-07-10 10:41:38 +00:00
if ( mb - > is_shift_pressed ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) + ( 100 * mb - > get_factor ( ) ) ) ;
} else if ( mb - > is_alt_pressed ( ) ) {
// Scroll 5 times as fast as normal (like in Visual Studio Code).
2023-02-17 13:50:46 +00:00
_scroll_down ( 15 * mb - > get_factor ( ) , true ) ;
2021-07-10 10:41:38 +00:00
} else if ( v_scroll - > is_visible ( ) ) {
// Scroll 3 lines.
2023-02-17 13:50:46 +00:00
_scroll_down ( 3 * mb - > get_factor ( ) , true ) ;
2018-01-03 22:21:54 +00:00
}
2017-05-20 15:38:03 +00:00
}
2021-08-13 21:31:57 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_LEFT ) {
2021-07-10 10:41:38 +00:00
h_scroll - > set_value ( h_scroll - > get_value ( ) - ( 100 * mb - > get_factor ( ) ) ) ;
2017-05-20 15:38:03 +00:00
}
2021-08-13 21:31:57 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_RIGHT ) {
2021-07-10 10:41:38 +00:00
h_scroll - > set_value ( h_scroll - > get_value ( ) + ( 100 * mb - > get_factor ( ) ) ) ;
2017-05-20 15:38:03 +00:00
}
2024-01-22 23:26:16 +00:00
2021-08-13 21:31:57 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
2021-07-10 10:41:38 +00:00
_reset_caret_blink_timer ( ) ;
2017-05-20 15:38:03 +00:00
2024-01-22 17:34:12 +00:00
apply_ime ( ) ;
2021-09-23 14:58:43 +00:00
Point2i pos = get_line_column_at_pos ( mpos ) ;
2024-01-22 23:26:16 +00:00
int line = pos . y ;
2021-07-10 10:41:38 +00:00
int col = pos . x ;
2016-05-09 18:21:55 +00:00
2024-01-22 23:26:16 +00:00
// Gutters.
2023-04-03 16:01:10 +00:00
int left_margin = theme_cache . style_normal - > get_margin ( SIDE_LEFT ) ;
2021-07-10 10:41:38 +00:00
for ( int i = 0 ; i < gutters . size ( ) ; i + + ) {
if ( ! gutters [ i ] . draw | | gutters [ i ] . width < = 0 ) {
continue ;
}
2020-07-25 00:15:23 +00:00
2022-09-14 22:54:14 +00:00
if ( mpos . x > = left_margin & & mpos . x < = left_margin + gutters [ i ] . width ) {
2024-01-22 23:26:16 +00:00
emit_signal ( SNAME ( " gutter_clicked " ) , line , i ) ;
2021-07-10 10:41:38 +00:00
return ;
}
2020-07-25 00:15:23 +00:00
2021-07-10 10:41:38 +00:00
left_margin + = gutters [ i ] . width ;
}
2017-11-12 23:12:17 +00:00
2024-01-22 23:26:16 +00:00
// Minimap.
2021-07-10 10:41:38 +00:00
if ( draw_minimap ) {
_update_minimap_click ( ) ;
if ( dragging_minimap ) {
return ;
}
}
2019-08-11 18:31:19 +00:00
2024-01-22 23:26:16 +00:00
// Update caret.
2022-06-03 16:47:37 +00:00
int caret = carets . size ( ) - 1 ;
int prev_col = get_caret_column ( caret ) ;
int prev_line = get_caret_line ( caret ) ;
2024-01-22 23:26:16 +00:00
int mouse_over_selection_caret = get_selection_at_line_column ( line , col , true ) ;
2022-06-03 16:47:37 +00:00
const int triple_click_timeout = 600 ;
const int triple_click_tolerance = 5 ;
bool is_triple_click = ( ! mb - > is_double_click ( ) & & ( OS : : get_singleton ( ) - > get_ticks_msec ( ) - last_dblclk ) < triple_click_timeout & & mb - > get_position ( ) . distance_to ( last_dblclk_pos ) < triple_click_tolerance ) ;
2023-08-24 09:15:30 +00:00
if ( ! mb - > is_double_click ( ) & & ! is_triple_click ) {
2022-06-03 16:47:37 +00:00
if ( mb - > is_alt_pressed ( ) ) {
2024-01-22 23:26:16 +00:00
prev_line = line ;
2022-06-03 16:47:37 +00:00
prev_col = col ;
2023-08-24 09:15:30 +00:00
// Remove caret at clicked location.
2024-01-22 23:26:16 +00:00
if ( get_caret_count ( ) > 1 ) {
2024-01-22 23:27:19 +00:00
// Deselect if clicked on caret or its selection.
int clicked_caret = get_selection_at_line_column ( line , col , true , false ) ;
if ( clicked_caret ! = - 1 ) {
remove_caret ( clicked_caret ) ;
last_dblclk = 0 ;
return ;
2023-08-24 09:15:30 +00:00
}
}
2024-01-22 23:26:16 +00:00
if ( mouse_over_selection_caret > = 0 ) {
// Did not remove selection under mouse, don't add a new caret.
2023-08-24 09:15:30 +00:00
return ;
}
2024-01-22 23:26:16 +00:00
// Create new caret at clicked location.
caret = add_caret ( line , col ) ;
2022-06-03 16:47:37 +00:00
if ( caret = = - 1 ) {
return ;
}
last_dblclk = 0 ;
2024-01-22 23:26:16 +00:00
} else if ( ! mb - > is_shift_pressed ( ) ) {
if ( drag_and_drop_selection_enabled & & mouse_over_selection_caret > = 0 ) {
// Try to drag and drop.
set_selection_mode ( SelectionMode : : SELECTION_MODE_NONE ) ;
selection_drag_attempt = true ;
drag_and_drop_origin_caret_index = mouse_over_selection_caret ;
last_dblclk = 0 ;
// Don't update caret until we know if it is not drag and drop.
return ;
2021-07-10 10:41:38 +00:00
} else {
2024-01-22 23:26:16 +00:00
// A regular click clears all other carets.
caret = 0 ;
remove_secondary_carets ( ) ;
deselect ( ) ;
2021-07-10 10:41:38 +00:00
}
}
2015-12-09 18:56:41 +00:00
2024-01-22 23:26:16 +00:00
_push_current_op ( ) ;
set_caret_line ( line , false , true , - 1 , caret ) ;
set_caret_column ( col , false , caret ) ;
2022-04-29 15:23:32 +00:00
selection_drag_attempt = false ;
2024-01-22 23:26:16 +00:00
bool caret_moved = get_caret_column ( caret ) ! = prev_col | | get_caret_line ( caret ) ! = prev_line ;
if ( selecting_enabled & & mb - > is_shift_pressed ( ) & & ! has_selection ( caret ) & & caret_moved ) {
// Select from the previous caret position.
select ( prev_line , prev_col , line , col , caret ) ;
}
// Start regular select mode.
set_selection_mode ( SelectionMode : : SELECTION_MODE_POINTER ) ;
_update_selection_mode_pointer ( true ) ;
} else if ( is_triple_click ) {
// Start triple-click select line mode.
set_selection_mode ( SelectionMode : : SELECTION_MODE_LINE ) ;
_update_selection_mode_line ( true ) ;
2021-07-10 10:41:38 +00:00
last_dblclk = 0 ;
2024-01-22 23:26:16 +00:00
} else if ( mb - > is_double_click ( ) ) {
// Start double-click select word mode.
set_selection_mode ( SelectionMode : : SELECTION_MODE_WORD ) ;
_update_selection_mode_word ( true ) ;
2021-07-10 10:41:38 +00:00
last_dblclk = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
last_dblclk_pos = mb - > get_position ( ) ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2017-12-20 01:36:47 +00:00
2021-08-13 21:31:57 +00:00
if ( is_middle_mouse_paste_enabled ( ) & & mb - > get_button_index ( ) = = MouseButton : : MIDDLE & & DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
2024-01-22 17:34:12 +00:00
apply_ime ( ) ;
2021-10-12 07:43:50 +00:00
paste_primary_clipboard ( ) ;
}
2022-04-22 16:52:31 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : RIGHT & & ( context_menu_enabled | | is_move_caret_on_right_click_enabled ( ) ) ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2021-07-10 10:41:38 +00:00
_reset_caret_blink_timer ( ) ;
2024-01-22 17:34:12 +00:00
apply_ime ( ) ;
2024-01-22 23:26:16 +00:00
_cancel_drag_and_drop_text ( ) ;
2017-12-20 01:36:47 +00:00
2021-09-23 14:58:43 +00:00
Point2i pos = get_line_column_at_pos ( mpos ) ;
2024-01-22 23:26:16 +00:00
int mouse_line = pos . y ;
int mouse_column = pos . x ;
2019-08-30 14:19:25 +00:00
2021-07-10 10:41:38 +00:00
if ( is_move_caret_on_right_click_enabled ( ) ) {
2024-01-22 23:26:16 +00:00
bool selection_clicked = get_selection_at_line_column ( mouse_line , mouse_column , true ) > = 0 ;
2023-04-30 16:28:21 +00:00
if ( ! selection_clicked ) {
deselect ( ) ;
remove_secondary_carets ( ) ;
2024-01-22 23:26:16 +00:00
set_caret_line ( mouse_line , false , false , - 1 ) ;
set_caret_column ( mouse_column ) ;
2021-07-10 10:41:38 +00:00
}
}
2016-03-08 23:00:52 +00:00
2022-04-22 16:52:31 +00:00
if ( context_menu_enabled ) {
2023-01-26 17:59:41 +00:00
_update_context_menu ( ) ;
2022-04-22 16:52:31 +00:00
menu - > set_position ( get_screen_position ( ) + mpos ) ;
menu - > reset_size ( ) ;
menu - > popup ( ) ;
grab_focus ( ) ;
}
2021-07-10 10:41:38 +00:00
}
} else {
2024-01-22 17:34:12 +00:00
if ( has_ime_text ( ) ) {
// Ignore mouse up in IME input mode.
return ;
}
2021-08-13 21:31:57 +00:00
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
2024-01-22 23:26:16 +00:00
if ( ! drag_action & & selection_drag_attempt & & is_mouse_over_selection ( ) ) {
// This is not a drag and drop attempt, update the caret.
selection_drag_attempt = false ;
2022-11-05 08:37:19 +00:00
remove_secondary_carets ( ) ;
2024-01-22 23:26:16 +00:00
deselect ( ) ;
2022-11-05 08:37:19 +00:00
Point2i pos = get_line_column_at_pos ( get_local_mouse_pos ( ) ) ;
2024-01-22 23:26:16 +00:00
set_caret_line ( pos . y , false , true , - 1 , 0 ) ;
2022-11-05 08:37:19 +00:00
set_caret_column ( pos . x , true , 0 ) ;
2021-11-24 21:22:40 +00:00
}
2021-07-10 10:41:38 +00:00
dragging_minimap = false ;
dragging_selection = false ;
can_drag_minimap = false ;
click_select_held - > stop ( ) ;
2021-10-20 19:25:09 +00:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
DisplayServer : : get_singleton ( ) - > clipboard_set_primary ( get_selected_text ( ) ) ;
}
2021-07-10 10:41:38 +00:00
}
2019-05-21 16:53:29 +00:00
2021-07-10 10:41:38 +00:00
// Notify to show soft keyboard.
notification ( NOTIFICATION_FOCUS_ENTER ) ;
}
2017-11-01 20:49:39 +00:00
}
2021-07-10 10:41:38 +00:00
const Ref < InputEventPanGesture > pan_gesture = p_gui_input ;
if ( pan_gesture . is_valid ( ) ) {
const real_t delta = pan_gesture - > get_delta ( ) . y ;
if ( delta < 0 ) {
2023-02-17 13:50:46 +00:00
_scroll_up ( - delta , false ) ;
2021-07-10 10:41:38 +00:00
} else {
2023-02-17 13:50:46 +00:00
_scroll_down ( delta , false ) ;
2021-07-10 10:41:38 +00:00
}
h_scroll - > set_value ( h_scroll - > get_value ( ) + pan_gesture - > get_delta ( ) . x * 100 ) ;
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) {
accept_event ( ) ; // Accept event if scroll changed.
}
2021-06-15 14:05:01 +00:00
return ;
}
2021-07-10 10:41:38 +00:00
Ref < InputEventMouseMotion > mm = p_gui_input ;
2020-12-07 11:32:00 +00:00
2021-07-10 10:41:38 +00:00
if ( mm . is_valid ( ) ) {
Vector2i mpos = mm - > get_position ( ) ;
if ( is_layout_rtl ( ) ) {
mpos . x = get_size ( ) . x - mpos . x ;
2017-05-20 15:38:03 +00:00
}
2016-09-12 13:52:29 +00:00
2024-01-22 23:26:16 +00:00
if ( mm - > get_button_mask ( ) . has_flag ( MouseButtonMask : : LEFT ) & & get_viewport ( ) - > gui_get_drag_data ( ) = = Variant ( ) ) {
// Update if not in drag and drop.
2021-07-10 10:41:38 +00:00
_reset_caret_blink_timer ( ) ;
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
if ( draw_minimap & & ! dragging_selection ) {
_update_minimap_drag ( ) ;
}
2019-05-21 16:53:29 +00:00
2024-01-22 17:34:12 +00:00
if ( ! dragging_minimap & & ! has_ime_text ( ) ) {
2022-04-29 15:23:32 +00:00
switch ( selecting_mode ) {
2021-07-10 10:41:38 +00:00
case SelectionMode : : SELECTION_MODE_POINTER : {
_update_selection_mode_pointer ( ) ;
} break ;
case SelectionMode : : SELECTION_MODE_WORD : {
_update_selection_mode_word ( ) ;
} break ;
case SelectionMode : : SELECTION_MODE_LINE : {
_update_selection_mode_line ( ) ;
} break ;
default : {
break ;
}
}
}
}
2021-09-29 09:37:27 +00:00
// Check if user is hovering a different gutter, and update if yes.
Vector2i current_hovered_gutter = Vector2i ( - 1 , - 1 ) ;
2023-04-03 16:01:10 +00:00
int left_margin = theme_cache . style_normal - > get_margin ( SIDE_LEFT ) ;
2021-09-29 09:37:27 +00:00
if ( mpos . x < = left_margin + gutters_width + gutter_padding ) {
int hovered_row = get_line_column_at_pos ( mpos ) . y ;
for ( int i = 0 ; i < gutters . size ( ) ; i + + ) {
if ( ! gutters [ i ] . draw | | gutters [ i ] . width < = 0 ) {
continue ;
}
2022-09-14 22:54:14 +00:00
if ( mpos . x > = left_margin & & mpos . x < left_margin + gutters [ i ] . width ) {
2021-09-29 09:37:27 +00:00
// We are in this gutter i's horizontal area.
current_hovered_gutter = Vector2i ( i , hovered_row ) ;
break ;
}
left_margin + = gutters [ i ] . width ;
}
}
if ( current_hovered_gutter ! = hovered_gutter ) {
hovered_gutter = current_hovered_gutter ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-09-29 09:37:27 +00:00
}
2021-11-24 21:22:40 +00:00
if ( drag_action & & can_drop_data ( mpos , get_viewport ( ) - > gui_get_drag_data ( ) ) ) {
2024-01-22 17:34:12 +00:00
apply_ime ( ) ;
2024-01-22 23:26:16 +00:00
// Update drag and drop caret.
2021-11-24 21:22:40 +00:00
drag_caret_force_displayed = true ;
Point2i pos = get_line_column_at_pos ( get_local_mouse_pos ( ) ) ;
2024-01-22 23:26:16 +00:00
if ( drag_caret_index = = - 1 ) {
// Force create a new caret for drag and drop.
carets . push_back ( Caret ( ) ) ;
drag_caret_index = carets . size ( ) - 1 ;
}
drag_caret_force_displayed = true ;
set_caret_line ( pos . y , false , true , - 1 , drag_caret_index ) ;
set_caret_column ( pos . x , true , drag_caret_index ) ;
2021-11-24 21:22:40 +00:00
dragging_selection = true ;
}
2020-12-07 11:32:00 +00:00
}
2016-03-08 23:00:52 +00:00
2020-02-02 18:47:33 +00:00
if ( draw_minimap & & ! dragging_selection ) {
_update_minimap_hover ( ) ;
}
2021-07-10 10:41:38 +00:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) {
accept_event ( ) ; // Accept event if scroll changed.
2020-12-07 11:32:00 +00:00
}
2016-09-12 13:52:29 +00:00
2021-07-10 10:41:38 +00:00
Ref < InputEventKey > k = p_gui_input ;
2016-09-12 13:52:29 +00:00
2021-07-10 10:41:38 +00:00
if ( k . is_valid ( ) ) {
2021-11-29 14:24:02 +00:00
if ( alt_input ( p_gui_input ) ) {
accept_event ( ) ;
return ;
}
2021-07-10 10:41:38 +00:00
if ( ! k - > is_pressed ( ) ) {
return ;
}
2020-12-07 11:32:00 +00:00
2021-07-10 10:41:38 +00:00
// If a modifier has been pressed, and nothing else, return.
2023-02-11 05:03:25 +00:00
if ( k - > get_keycode ( ) = = Key : : CTRL | | k - > get_keycode ( ) = = Key : : ALT | | k - > get_keycode ( ) = = Key : : SHIFT | | k - > get_keycode ( ) = = Key : : META | | k - > get_keycode ( ) = = Key : : CAPSLOCK ) {
2021-07-10 10:41:38 +00:00
return ;
2020-12-07 11:32:00 +00:00
}
2018-04-05 16:58:15 +00:00
2024-01-22 23:26:16 +00:00
_cancel_drag_and_drop_text ( ) ;
2020-12-07 11:32:00 +00:00
_reset_caret_blink_timer ( ) ;
2018-04-05 16:58:15 +00:00
2020-12-07 11:32:00 +00:00
// Allow unicode handling if:
2023-02-11 05:03:25 +00:00
// * No modifiers are pressed (except Shift and CapsLock)
2023-06-08 21:24:00 +00:00
bool allow_unicode_handling = ! ( k - > is_ctrl_pressed ( ) | | k - > is_alt_pressed ( ) | | k - > is_meta_pressed ( ) ) ;
2020-09-18 07:36:10 +00:00
2023-03-08 22:00:27 +00:00
// Check and handle all built-in shortcuts.
2016-03-15 12:02:38 +00:00
2020-12-07 11:32:00 +00:00
// NEWLINES.
if ( k - > is_action ( " ui_text_newline_above " , true ) ) {
_new_line ( false , true ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_newline_blank " , true ) ) {
_new_line ( false ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_newline " , true ) ) {
_new_line ( ) ;
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2020-12-07 11:32:00 +00:00
// BACKSPACE AND DELETE.
if ( k - > is_action ( " ui_text_backspace_all_to_left " , true ) ) {
2021-06-15 14:05:01 +00:00
_do_backspace ( false , true ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_backspace_word " , true ) ) {
2021-06-15 14:05:01 +00:00
_do_backspace ( true ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_backspace " , true ) ) {
2021-06-15 14:05:01 +00:00
_do_backspace ( ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_delete_all_to_right " , true ) ) {
_delete ( false , true ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_delete_word " , true ) ) {
_delete ( true ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_delete " , true ) ) {
_delete ( ) ;
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2020-12-07 11:32:00 +00:00
// SCROLLING.
if ( k - > is_action ( " ui_text_scroll_up " , true ) ) {
_scroll_lines_up ( ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_scroll_down " , true ) ) {
_scroll_lines_down ( ) ;
accept_event ( ) ;
return ;
}
2019-06-22 18:22:52 +00:00
2022-04-08 13:13:32 +00:00
if ( is_shortcut_keys_enabled ( ) ) {
2024-02-02 22:10:55 +00:00
// SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, SKIP SELECTION FOR NEXT OCCURRENCE,
2022-11-02 14:14:08 +00:00
// CLEAR CARETS AND SELECTIONS, CUT, COPY, PASTE.
2022-04-08 13:13:32 +00:00
if ( k - > is_action ( " ui_text_select_all " , true ) ) {
select_all ( ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_select_word_under_caret " , true ) ) {
select_word_under_caret ( ) ;
accept_event ( ) ;
return ;
}
2022-10-19 17:35:14 +00:00
if ( k - > is_action ( " ui_text_add_selection_for_next_occurrence " , true ) ) {
add_selection_for_next_occurrence ( ) ;
accept_event ( ) ;
return ;
}
2024-02-02 22:10:55 +00:00
if ( k - > is_action ( " ui_text_skip_selection_for_next_occurrence " , true ) ) {
skip_selection_for_next_occurrence ( ) ;
accept_event ( ) ;
return ;
}
2022-11-02 14:14:08 +00:00
if ( k - > is_action ( " ui_text_clear_carets_and_selection " , true ) ) {
// Since the default shortcut is ESC, accepts the event only if it's actually performed.
if ( _clear_carets_and_selection ( ) ) {
accept_event ( ) ;
return ;
}
2022-10-31 15:55:47 +00:00
}
2022-04-08 13:13:32 +00:00
if ( k - > is_action ( " ui_cut " , true ) ) {
cut ( ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_copy " , true ) ) {
copy ( ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_paste " , true ) ) {
paste ( ) ;
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2022-04-08 13:13:32 +00:00
// UNDO/REDO.
if ( k - > is_action ( " ui_undo " , true ) ) {
undo ( ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_redo " , true ) ) {
redo ( ) ;
accept_event ( ) ;
return ;
}
2022-10-09 15:07:42 +00:00
if ( k - > is_action ( " ui_text_caret_add_below " , true ) ) {
add_caret_at_carets ( true ) ;
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_add_above " , true ) ) {
add_caret_at_carets ( false ) ;
accept_event ( ) ;
return ;
}
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2020-12-07 11:32:00 +00:00
// MISC.
if ( k - > is_action ( " ui_menu " , true ) ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2020-12-07 11:32:00 +00:00
if ( context_menu_enabled ) {
2023-01-26 17:59:41 +00:00
_update_context_menu ( ) ;
2021-07-08 17:35:56 +00:00
adjust_viewport_to_caret ( ) ;
2021-08-31 15:43:35 +00:00
menu - > set_position ( get_screen_position ( ) + get_caret_draw_pos ( ) ) ;
2021-11-20 08:04:57 +00:00
menu - > reset_size ( ) ;
2020-12-07 11:32:00 +00:00
menu - > popup ( ) ;
menu - > grab_focus ( ) ;
}
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_toggle_insert_mode " , true ) ) {
2021-07-09 12:40:05 +00:00
set_overtype_mode_enabled ( ! overtype_mode ) ;
2017-05-20 15:38:03 +00:00
accept_event ( ) ;
return ;
}
2020-12-07 11:32:00 +00:00
if ( k - > is_action ( " ui_swap_input_direction " , true ) ) {
_swap_current_input_direction ( ) ;
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2021-07-08 17:35:56 +00:00
// CARET MOVEMENT
2016-03-31 19:49:30 +00:00
2020-12-07 11:32:00 +00:00
k = k - > duplicate ( ) ;
2021-04-24 20:33:50 +00:00
bool shift_pressed = k - > is_shift_pressed ( ) ;
2020-12-07 11:32:00 +00:00
// Remove shift or else actions will not match. Use above variable for selection.
2021-04-24 20:33:50 +00:00
k - > set_shift_pressed ( false ) ;
2016-03-08 23:00:52 +00:00
2021-07-08 17:35:56 +00:00
// CARET MOVEMENT - LEFT, RIGHT.
2020-12-07 11:32:00 +00:00
if ( k - > is_action ( " ui_text_caret_word_left " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_left ( shift_pressed , true ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_left " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_left ( shift_pressed , false ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_word_right " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_right ( shift_pressed , true ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_right " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_right ( shift_pressed , false ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2021-07-08 17:35:56 +00:00
// CARET MOVEMENT - UP, DOWN.
2020-12-07 11:32:00 +00:00
if ( k - > is_action ( " ui_text_caret_up " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_up ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_down " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_down ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
2016-04-05 12:52:18 +00:00
2021-07-08 17:35:56 +00:00
// CARET MOVEMENT - DOCUMENT START/END.
2020-12-07 11:32:00 +00:00
if ( k - > is_action ( " ui_text_caret_document_start " , true ) ) { // && shift_pressed) {
2021-07-08 17:35:56 +00:00
_move_caret_document_start ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_document_end " , true ) ) { // && shift_pressed) {
2021-07-08 17:35:56 +00:00
_move_caret_document_end ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2021-07-08 17:35:56 +00:00
// CARET MOVEMENT - LINE START/END.
2020-12-07 11:32:00 +00:00
if ( k - > is_action ( " ui_text_caret_line_start " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_to_line_start ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_line_end " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_to_line_end ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
2017-05-20 15:38:03 +00:00
2021-07-08 17:35:56 +00:00
// CARET MOVEMENT - PAGE UP/DOWN.
2020-12-07 11:32:00 +00:00
if ( k - > is_action ( " ui_text_caret_page_up " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_page_up ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
if ( k - > is_action ( " ui_text_caret_page_down " , true ) ) {
2021-07-08 17:35:56 +00:00
_move_caret_page_down ( shift_pressed ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2022-10-09 16:46:40 +00:00
// Handle tab as it has no set unicode value.
if ( k - > is_action ( " ui_text_indent " , true ) ) {
if ( editable ) {
insert_text_at_caret ( " \t " ) ;
}
accept_event ( ) ;
return ;
}
// Handle Unicode (if no modifiers active).
if ( allow_unicode_handling & & editable & & k - > get_unicode ( ) > = 32 ) {
2021-06-28 16:14:44 +00:00
handle_unicode_input ( k - > get_unicode ( ) ) ;
2020-12-07 11:32:00 +00:00
accept_event ( ) ;
return ;
}
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
/* Input actions. */
void TextEdit : : _swap_current_input_direction ( ) {
if ( input_direction = = TEXT_DIRECTION_LTR ) {
input_direction = TEXT_DIRECTION_RTL ;
} else {
input_direction = TEXT_DIRECTION_LTR ;
2015-01-02 18:08:40 +00:00
}
2022-06-03 16:47:37 +00:00
for ( int i = 0 ; i < carets . size ( ) ; i + + ) {
set_caret_column ( get_caret_column ( i ) , i = = 0 , i ) ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _new_line ( bool p_split_current_line , bool p_above ) {
if ( ! editable ) {
2015-01-02 18:08:40 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
2017-11-12 23:12:17 +00:00
2024-01-22 23:27:19 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( multicaret_edit_ignore_caret ( i ) ) {
continue ;
}
if ( p_split_current_line ) {
insert_text_at_caret ( " \n " , i ) ;
} else {
int line = get_caret_line ( i ) ;
insert_text ( " \n " , line , p_above ? 0 : text [ line ] . length ( ) , p_above , p_above ) ;
deselect ( i ) ;
set_caret_line ( p_above ? line : line + 1 , false , true , - 1 , i ) ;
set_caret_column ( 0 , i = = 0 , i ) ;
2022-06-03 16:47:37 +00:00
}
2019-08-25 13:49:13 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2021-07-10 10:41:38 +00:00
end_complex_operation ( ) ;
2019-08-25 13:49:13 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_left ( bool p_select , bool p_move_by_word ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
// Handle selection.
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else if ( has_selection ( i ) & & ! p_move_by_word ) {
// If a selection is active, move caret to start of selection.
2024-01-22 23:26:16 +00:00
set_caret_line ( get_selection_from_line ( i ) , false , true , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( get_selection_from_column ( i ) , i = = 0 , i ) ;
deselect ( i ) ;
continue ;
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
deselect ( i ) ;
}
if ( p_move_by_word ) {
int cc = get_caret_column ( i ) ;
// If the caret is at the start of the line, and not on the first line, move it up to the end of the previous line.
if ( cc = = 0 & & get_caret_line ( i ) > 0 ) {
2024-01-22 23:26:16 +00:00
set_caret_line ( get_caret_line ( i ) - 1 , false , true , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( text [ get_caret_line ( i ) ] . length ( ) , i = = 0 , i ) ;
2022-02-21 21:32:24 +00:00
} else {
2022-06-03 16:47:37 +00:00
PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( get_caret_line ( i ) ) - > get_rid ( ) ) ;
if ( words . is_empty ( ) | | cc < = words [ 0 ] ) {
// This solves the scenario where there are no words but glyfs that can be ignored.
cc = 0 ;
} else {
for ( int j = words . size ( ) - 2 ; j > = 0 ; j = j - 2 ) {
if ( words [ j ] < cc ) {
cc = words [ j ] ;
break ;
}
2022-02-21 21:32:24 +00:00
}
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
set_caret_column ( cc , i = = 0 , i ) ;
2021-07-10 10:41:38 +00:00
}
} else {
2022-06-03 16:47:37 +00:00
// If the caret is at the start of the line, and not on the first line, move it up to the end of the previous line.
if ( get_caret_column ( i ) = = 0 ) {
if ( get_caret_line ( i ) > 0 ) {
2024-01-22 23:26:16 +00:00
int new_caret_line = get_caret_line ( i ) - get_next_visible_line_offset_from ( CLAMP ( get_caret_line ( i ) - 1 , 0 , text . size ( ) - 1 ) , - 1 ) ;
set_caret_line ( new_caret_line , false , true , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( text [ get_caret_line ( i ) ] . length ( ) , i = = 0 , i ) ;
}
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
if ( caret_mid_grapheme_enabled ) {
set_caret_column ( get_caret_column ( i ) - 1 , i = = 0 , i ) ;
} else {
2023-08-15 08:42:40 +00:00
set_caret_column ( TS - > shaped_text_prev_character_pos ( text . get_line_data ( get_caret_line ( i ) ) - > get_rid ( ) , get_caret_column ( i ) ) , i = = 0 , i ) ;
2022-06-03 16:47:37 +00:00
}
2021-07-10 10:41:38 +00:00
}
}
2020-12-07 11:32:00 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-10 10:41:38 +00:00
}
2020-12-07 11:32:00 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_right ( bool p_select , bool p_move_by_word ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2023-03-08 22:00:27 +00:00
// Handle selection.
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else if ( has_selection ( i ) & & ! p_move_by_word ) {
2023-03-08 22:00:27 +00:00
// If a selection is active, move caret to end of selection.
2024-01-22 23:26:16 +00:00
set_caret_line ( get_selection_to_line ( i ) , false , true , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( get_selection_to_column ( i ) , i = = 0 , i ) ;
deselect ( i ) ;
continue ;
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
deselect ( i ) ;
}
if ( p_move_by_word ) {
int cc = get_caret_column ( i ) ;
// If the caret is at the end of the line, and not on the last line, move it down to the beginning of the next line.
if ( cc = = text [ get_caret_line ( i ) ] . length ( ) & & get_caret_line ( i ) < text . size ( ) - 1 ) {
2024-01-22 23:26:16 +00:00
set_caret_line ( get_caret_line ( i ) + 1 , false , true , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( 0 , i = = 0 , i ) ;
2022-02-21 21:32:24 +00:00
} else {
2022-06-03 16:47:37 +00:00
PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( get_caret_line ( i ) ) - > get_rid ( ) ) ;
if ( words . is_empty ( ) | | cc > = words [ words . size ( ) - 1 ] ) {
// This solves the scenario where there are no words but glyfs that can be ignored.
cc = text [ get_caret_line ( i ) ] . length ( ) ;
} else {
for ( int j = 1 ; j < words . size ( ) ; j = j + 2 ) {
if ( words [ j ] > cc ) {
cc = words [ j ] ;
break ;
}
2022-02-21 21:32:24 +00:00
}
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
set_caret_column ( cc , i = = 0 , i ) ;
2021-07-10 10:41:38 +00:00
}
} else {
2022-06-03 16:47:37 +00:00
// If we are at the end of the line, move the caret to the next line down.
if ( get_caret_column ( i ) = = text [ get_caret_line ( i ) ] . length ( ) ) {
if ( get_caret_line ( i ) < text . size ( ) - 1 ) {
2024-01-22 23:26:16 +00:00
int new_caret_line = get_caret_line ( i ) + get_next_visible_line_offset_from ( CLAMP ( get_caret_line ( i ) + 1 , 0 , text . size ( ) - 1 ) , 1 ) ;
set_caret_line ( new_caret_line , false , false , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( 0 , i = = 0 , i ) ;
}
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
if ( caret_mid_grapheme_enabled ) {
set_caret_column ( get_caret_column ( i ) + 1 , i = = 0 , i ) ;
} else {
2023-08-15 08:42:40 +00:00
set_caret_column ( TS - > shaped_text_next_character_pos ( text . get_line_data ( get_caret_line ( i ) ) - > get_rid ( ) , get_caret_column ( i ) ) , i = = 0 , i ) ;
2022-06-03 16:47:37 +00:00
}
2021-07-10 10:41:38 +00:00
}
2021-07-09 16:27:09 +00:00
}
2020-05-14 14:41:43 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-09 16:27:09 +00:00
}
2017-11-16 04:00:27 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_up ( bool p_select ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else {
deselect ( i ) ;
}
2021-07-09 16:27:09 +00:00
2022-06-03 16:47:37 +00:00
int cur_wrap_index = get_caret_wrap_index ( i ) ;
if ( cur_wrap_index > 0 ) {
set_caret_line ( get_caret_line ( i ) , true , false , cur_wrap_index - 1 , i ) ;
} else if ( get_caret_line ( i ) = = 0 ) {
set_caret_column ( 0 , i = = 0 , i ) ;
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
int new_line = get_caret_line ( i ) - get_next_visible_line_offset_from ( get_caret_line ( i ) - 1 , - 1 ) ;
if ( is_line_wrapped ( new_line ) ) {
set_caret_line ( new_line , i = = 0 , false , get_line_wrap_count ( new_line ) , i ) ;
} else {
set_caret_line ( new_line , i = = 0 , false , 0 , i ) ;
}
2018-01-26 01:41:17 +00:00
}
2021-07-09 16:27:09 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_down ( bool p_select ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else {
deselect ( i ) ;
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
int cur_wrap_index = get_caret_wrap_index ( i ) ;
if ( cur_wrap_index < get_line_wrap_count ( get_caret_line ( i ) ) ) {
set_caret_line ( get_caret_line ( i ) , i = = 0 , false , cur_wrap_index + 1 , i ) ;
} else if ( get_caret_line ( i ) = = get_last_unhidden_line ( ) ) {
set_caret_column ( text [ get_caret_line ( i ) ] . length ( ) ) ;
} else {
int new_line = get_caret_line ( i ) + get_next_visible_line_offset_from ( CLAMP ( get_caret_line ( i ) + 1 , 0 , text . size ( ) - 1 ) , 1 ) ;
set_caret_line ( new_line , i = = 0 , false , 0 , i ) ;
}
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_to_line_start ( bool p_select ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else {
deselect ( i ) ;
}
2020-07-27 12:54:12 +00:00
2022-06-03 16:47:37 +00:00
// Move caret column to start of wrapped row and then to start of text.
Vector < String > rows = get_line_wrapped_text ( get_caret_line ( i ) ) ;
int wi = get_caret_wrap_index ( i ) ;
int row_start_col = 0 ;
for ( int j = 0 ; j < wi ; j + + ) {
row_start_col + = rows [ j ] . length ( ) ;
}
if ( get_caret_column ( i ) = = row_start_col | | wi = = 0 ) {
// Compute whitespace symbols sequence length.
int current_line_whitespace_len = get_first_non_whitespace_column ( get_caret_line ( i ) ) ;
if ( get_caret_column ( i ) = = current_line_whitespace_len ) {
set_caret_column ( 0 , i = = 0 , i ) ;
} else {
set_caret_column ( current_line_whitespace_len , i = = 0 , i ) ;
}
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
set_caret_column ( row_start_col , i = = 0 , i ) ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-10 10:41:38 +00:00
}
void TextEdit : : _move_caret_to_line_end ( bool p_select ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else {
deselect ( i ) ;
}
2021-07-10 10:41:38 +00:00
2022-06-03 16:47:37 +00:00
// Move caret column to end of wrapped row and then to end of text.
Vector < String > rows = get_line_wrapped_text ( get_caret_line ( i ) ) ;
int wi = get_caret_wrap_index ( i ) ;
int row_end_col = - 1 ;
for ( int j = 0 ; j < wi + 1 ; j + + ) {
row_end_col + = rows [ j ] . length ( ) ;
}
if ( wi = = rows . size ( ) - 1 | | get_caret_column ( i ) = = row_end_col ) {
set_caret_column ( text [ get_caret_line ( i ) ] . length ( ) , i = = 0 , i ) ;
} else {
set_caret_column ( row_end_col , i = = 0 , i ) ;
}
2020-07-27 12:54:12 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2014-04-17 11:30:40 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_page_up ( bool p_select ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else {
deselect ( i ) ;
}
2019-03-09 00:26:49 +00:00
2022-06-03 16:47:37 +00:00
Point2i next_line = get_next_visible_line_index_offset_from ( get_caret_line ( i ) , get_caret_wrap_index ( i ) , - get_visible_line_count ( ) ) ;
int n_line = get_caret_line ( i ) - next_line . x + 1 ;
set_caret_line ( n_line , i = = 0 , false , next_line . y , i ) ;
2019-03-09 00:26:49 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2020-09-18 07:36:10 +00:00
}
2014-02-10 01:10:30 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_page_down ( bool p_select ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_select ) {
_pre_shift_selection ( i ) ;
} else {
deselect ( i ) ;
}
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
Point2i next_line = get_next_visible_line_index_offset_from ( get_caret_line ( i ) , get_caret_wrap_index ( i ) , get_visible_line_count ( ) ) ;
int n_line = get_caret_line ( i ) + next_line . x - 1 ;
set_caret_line ( n_line , i = = 0 , false , next_line . y , i ) ;
2020-09-18 07:36:10 +00:00
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2020-09-18 07:36:10 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _do_backspace ( bool p_word , bool p_all_to_left ) {
2022-06-03 16:47:37 +00:00
if ( ! editable ) {
2021-07-10 10:41:38 +00:00
return ;
}
2020-09-18 07:36:10 +00:00
2022-06-03 16:47:37 +00:00
start_action ( EditAction : : ACTION_BACKSPACE ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
2020-09-18 07:36:10 +00:00
2024-01-22 23:27:19 +00:00
Vector < int > sorted_carets = get_sorted_carets ( ) ;
sorted_carets . reverse ( ) ;
for ( int i = 0 ; i < sorted_carets . size ( ) ; i + + ) {
int caret_index = sorted_carets [ i ] ;
if ( multicaret_edit_ignore_caret ( caret_index ) ) {
2022-06-03 16:47:37 +00:00
continue ;
}
2020-09-18 07:36:10 +00:00
2024-01-22 23:27:19 +00:00
if ( get_caret_column ( caret_index ) = = 0 & & get_caret_line ( caret_index ) = = 0 & & ! has_selection ( caret_index ) ) {
2022-06-03 16:47:37 +00:00
continue ;
2022-02-21 21:32:24 +00:00
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
if ( has_selection ( caret_index ) | | ( ! p_all_to_left & & ! p_word ) | | get_caret_column ( caret_index ) = = 0 ) {
backspace ( caret_index ) ;
continue ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
if ( p_all_to_left ) {
// Remove everything to left of caret to the start of the line.
int caret_current_column = get_caret_column ( caret_index ) ;
_remove_text ( get_caret_line ( caret_index ) , 0 , get_caret_line ( caret_index ) , caret_current_column ) ;
collapse_carets ( get_caret_line ( caret_index ) , 0 , get_caret_line ( caret_index ) , caret_current_column ) ;
set_caret_column ( 0 , caret_index = = 0 , caret_index ) ;
_offset_carets_after ( get_caret_line ( caret_index ) , caret_current_column , get_caret_line ( caret_index ) , 0 ) ;
2022-06-03 16:47:37 +00:00
continue ;
2021-07-16 21:36:05 +00:00
}
2020-09-18 07:36:10 +00:00
2022-06-03 16:47:37 +00:00
if ( p_word ) {
2024-01-22 23:27:19 +00:00
// Remove text to the start of the word left of the caret.
int from_column = get_caret_column ( caret_index ) ;
int column = get_caret_column ( caret_index ) ;
2022-06-03 16:47:37 +00:00
// Check for the case "<word><space><caret>" and ignore the space.
// No need to check for column being 0 since it is checked above.
2024-01-22 23:27:19 +00:00
if ( is_whitespace ( text [ get_caret_line ( caret_index ) ] [ get_caret_column ( caret_index ) - 1 ] ) ) {
2022-06-03 16:47:37 +00:00
column - = 1 ;
}
2024-01-22 23:27:19 +00:00
2022-06-03 16:47:37 +00:00
// Get a list with the indices of the word bounds of the given text line.
2024-01-22 23:27:19 +00:00
const PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( get_caret_line ( caret_index ) ) - > get_rid ( ) ) ;
2022-06-03 16:47:37 +00:00
if ( words . is_empty ( ) | | column < = words [ 0 ] ) {
// If "words" is empty, meaning no words are left, we can remove everything until the beginning of the line.
column = 0 ;
} else {
// Otherwise search for the first word break that is smaller than the index from we're currently deleting.
for ( int c = words . size ( ) - 2 ; c > = 0 ; c = c - 2 ) {
if ( words [ c ] < column ) {
column = words [ c ] ;
break ;
}
}
}
2024-01-22 23:27:19 +00:00
_remove_text ( get_caret_line ( caret_index ) , column , get_caret_line ( caret_index ) , from_column ) ;
collapse_carets ( get_caret_line ( caret_index ) , column , get_caret_line ( caret_index ) , from_column ) ;
set_caret_column ( column , caret_index = = 0 , caret_index ) ;
_offset_carets_after ( get_caret_line ( caret_index ) , from_column , get_caret_line ( caret_index ) , column ) ;
2022-06-03 16:47:37 +00:00
}
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2022-06-03 16:47:37 +00:00
end_action ( ) ;
2020-09-18 07:36:10 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _delete ( bool p_word , bool p_all_to_right ) {
if ( ! editable ) {
return ;
2020-09-18 07:36:10 +00:00
}
2022-06-03 16:47:37 +00:00
start_action ( EditAction : : ACTION_DELETE ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
2020-09-18 07:36:10 +00:00
2024-01-22 23:27:19 +00:00
Vector < int > sorted_carets = get_sorted_carets ( ) ;
for ( int i = 0 ; i < sorted_carets . size ( ) ; i + + ) {
int caret_index = sorted_carets [ i ] ;
if ( multicaret_edit_ignore_caret ( caret_index ) ) {
continue ;
}
if ( has_selection ( caret_index ) ) {
delete_selection ( caret_index ) ;
2022-06-03 16:47:37 +00:00
continue ;
}
2020-09-18 07:36:10 +00:00
2024-01-22 23:27:19 +00:00
int curline_len = text [ get_caret_line ( caret_index ) ] . length ( ) ;
if ( get_caret_line ( caret_index ) = = text . size ( ) - 1 & & get_caret_column ( caret_index ) = = curline_len ) {
2022-06-03 16:47:37 +00:00
continue ; // Last line, last column: Nothing to do.
2022-04-22 16:52:31 +00:00
}
2024-01-22 23:27:19 +00:00
int next_line = get_caret_column ( caret_index ) < curline_len ? get_caret_line ( caret_index ) : get_caret_line ( caret_index ) + 1 ;
2022-06-03 16:47:37 +00:00
int next_column ;
2021-07-10 10:41:38 +00:00
2022-06-03 16:47:37 +00:00
if ( p_all_to_right ) {
2024-01-22 23:27:19 +00:00
if ( get_caret_column ( caret_index ) = = curline_len ) {
2022-06-03 16:47:37 +00:00
continue ;
}
2023-03-08 22:00:27 +00:00
// Delete everything to right of caret.
2022-06-03 16:47:37 +00:00
next_column = curline_len ;
2024-01-22 23:27:19 +00:00
next_line = get_caret_line ( caret_index ) ;
} else if ( p_word & & get_caret_column ( caret_index ) < curline_len - 1 ) {
2023-03-08 22:00:27 +00:00
// Delete next word to right of caret.
2024-01-22 23:27:19 +00:00
int line = get_caret_line ( caret_index ) ;
int column = get_caret_column ( caret_index ) ;
2022-06-03 16:47:37 +00:00
PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( line ) - > get_rid ( ) ) ;
for ( int j = 1 ; j < words . size ( ) ; j = j + 2 ) {
if ( words [ j ] > column ) {
column = words [ j ] ;
break ;
}
}
next_line = line ;
next_column = column ;
2021-07-10 10:41:38 +00:00
} else {
2023-03-08 22:00:27 +00:00
// Delete one character.
2022-06-03 16:47:37 +00:00
if ( caret_mid_grapheme_enabled ) {
2024-01-22 23:27:19 +00:00
next_column = get_caret_column ( caret_index ) < curline_len ? ( get_caret_column ( caret_index ) + 1 ) : 0 ;
2022-06-03 16:47:37 +00:00
} else {
2024-01-22 23:27:19 +00:00
next_column = get_caret_column ( caret_index ) < curline_len ? TS - > shaped_text_next_character_pos ( text . get_line_data ( get_caret_line ( caret_index ) ) - > get_rid ( ) , ( get_caret_column ( caret_index ) ) ) : 0 ;
2022-06-03 16:47:37 +00:00
}
2021-07-16 21:36:05 +00:00
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
_remove_text ( get_caret_line ( caret_index ) , get_caret_column ( caret_index ) , next_line , next_column ) ;
collapse_carets ( get_caret_line ( caret_index ) , get_caret_column ( caret_index ) , next_line , next_column ) ;
_offset_carets_after ( next_line , next_column , get_caret_line ( caret_index ) , get_caret_column ( caret_index ) ) ;
2020-09-18 07:36:10 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2022-06-03 16:47:37 +00:00
end_action ( ) ;
2020-09-18 07:36:10 +00:00
}
2014-12-17 01:31:57 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_document_start ( bool p_select ) {
2022-06-03 16:47:37 +00:00
remove_secondary_carets ( ) ;
2021-07-10 10:41:38 +00:00
if ( p_select ) {
2022-06-03 16:47:37 +00:00
_pre_shift_selection ( 0 ) ;
2021-07-10 10:41:38 +00:00
} else {
deselect ( ) ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2024-01-22 23:26:16 +00:00
set_caret_line ( 0 , false , true , - 1 ) ;
2021-07-10 10:41:38 +00:00
set_caret_column ( 0 ) ;
2021-03-13 14:09:49 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _move_caret_document_end ( bool p_select ) {
2022-06-03 16:47:37 +00:00
remove_secondary_carets ( ) ;
2021-07-10 10:41:38 +00:00
if ( p_select ) {
2022-06-03 16:47:37 +00:00
_pre_shift_selection ( 0 ) ;
2021-07-10 10:41:38 +00:00
} else {
deselect ( ) ;
}
2014-02-10 01:10:30 +00:00
2024-01-22 23:26:16 +00:00
set_caret_line ( get_last_unhidden_line ( ) , true , false , - 1 ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( text [ get_caret_line ( ) ] . length ( ) ) ;
2021-07-10 10:41:38 +00:00
}
2014-02-10 01:10:30 +00:00
2022-11-02 14:14:08 +00:00
bool TextEdit : : _clear_carets_and_selection ( ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2022-11-02 14:14:08 +00:00
if ( get_caret_count ( ) > 1 ) {
remove_secondary_carets ( ) ;
return true ;
}
if ( has_selection ( ) ) {
deselect ( ) ;
return true ;
}
return false ;
2022-10-31 15:55:47 +00:00
}
2022-01-23 20:07:35 +00:00
void TextEdit : : _update_placeholder ( ) {
2023-04-03 16:01:10 +00:00
if ( theme_cache . font . is_null ( ) | | theme_cache . font_size < = 0 ) {
2022-01-23 20:07:35 +00:00
return ; // Not in tree?
}
2023-10-25 14:51:17 +00:00
const String placeholder_translated = atr ( placeholder_text ) ;
2022-02-10 11:00:11 +00:00
// Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now.
2022-01-23 20:07:35 +00:00
placeholder_data_buf - > clear ( ) ;
placeholder_data_buf - > set_width ( text . get_width ( ) ) ;
2024-02-19 11:41:12 +00:00
BitField < TextServer : : LineBreakFlag > flags = text . get_brk_flags ( ) ;
if ( text . is_indent_wrapped_lines ( ) ) {
flags . set_flag ( TextServer : : BREAK_TRIM_INDENT ) ;
}
placeholder_data_buf - > set_break_flags ( flags ) ;
2023-09-07 05:25:58 +00:00
if ( text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
placeholder_data_buf - > set_direction ( is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ) ;
} else {
placeholder_data_buf - > set_direction ( ( TextServer : : Direction ) text_direction ) ;
}
2022-01-23 20:07:35 +00:00
placeholder_data_buf - > set_preserve_control ( draw_control_chars ) ;
2023-10-25 14:51:17 +00:00
placeholder_data_buf - > add_string ( placeholder_translated , theme_cache . font , theme_cache . font_size , language ) ;
2022-01-23 20:07:35 +00:00
2023-10-25 14:51:17 +00:00
placeholder_bidi_override = structured_text_parser ( st_parser , st_args , placeholder_translated ) ;
2022-01-23 20:07:35 +00:00
if ( placeholder_bidi_override . is_empty ( ) ) {
TS - > shaped_text_set_bidi_override ( placeholder_data_buf - > get_rid ( ) , placeholder_bidi_override ) ;
}
if ( get_tab_size ( ) > 0 ) {
Vector < float > tabs ;
2023-04-03 16:01:10 +00:00
tabs . push_back ( theme_cache . font - > get_char_size ( ' ' , theme_cache . font_size ) . width * get_tab_size ( ) ) ;
2022-01-23 20:07:35 +00:00
placeholder_data_buf - > tab_align ( tabs ) ;
}
// Update height.
const int wrap_amount = placeholder_data_buf - > get_line_count ( ) - 1 ;
2023-04-03 16:01:10 +00:00
placeholder_line_height = theme_cache . font - > get_height ( theme_cache . font_size ) ;
2022-01-23 20:07:35 +00:00
for ( int i = 0 ; i < = wrap_amount ; i + + ) {
placeholder_line_height = MAX ( placeholder_line_height , placeholder_data_buf - > get_line_size ( i ) . y ) ;
}
// Update width.
placeholder_max_width = placeholder_data_buf - > get_size ( ) . x ;
// Update wrapped rows.
placeholder_wraped_rows . clear ( ) ;
for ( int i = 0 ; i < = wrap_amount ; i + + ) {
Vector2i line_range = placeholder_data_buf - > get_line_range ( i ) ;
2023-10-25 14:51:17 +00:00
placeholder_wraped_rows . push_back ( placeholder_translated . substr ( line_range . x , line_range . y - line_range . x ) ) ;
2022-01-23 20:07:35 +00:00
}
}
2024-07-27 21:14:51 +00:00
bool TextEdit : : _using_placeholder ( ) const {
return text . size ( ) = = 1 & & text [ 0 ] . is_empty ( ) & & ime_text . is_empty ( ) ;
}
2023-04-03 16:01:10 +00:00
void TextEdit : : _update_theme_item_cache ( ) {
Control : : _update_theme_item_cache ( ) ;
theme_cache . base_scale = get_theme_default_base_scale ( ) ;
use_selected_font_color = theme_cache . font_selected_color ! = Color ( 0 , 0 , 0 , 0 ) ;
2021-07-09 11:42:55 +00:00
2023-04-30 02:54:50 +00:00
if ( text . get_line_height ( ) + theme_cache . line_spacing < 1 ) {
WARN_PRINT ( " Line height is too small, please increase font_size and/or line_spacing " ) ;
}
2023-04-03 16:01:10 +00:00
}
2020-09-18 07:36:10 +00:00
2023-04-03 16:01:10 +00:00
void TextEdit : : _update_caches ( ) {
2021-07-10 10:41:38 +00:00
/* Text properties. */
2020-09-18 07:36:10 +00:00
TextServer : : Direction dir ;
if ( text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
dir = is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ;
} else {
dir = ( TextServer : : Direction ) text_direction ;
}
2021-12-09 09:42:46 +00:00
text . set_direction_and_language ( dir , ( ! language . is_empty ( ) ) ? language : TranslationServer : : get_singleton ( ) - > get_tool_locale ( ) ) ;
2020-09-18 07:36:10 +00:00
text . set_draw_control_chars ( draw_control_chars ) ;
2023-04-03 16:01:10 +00:00
text . set_font ( theme_cache . font ) ;
text . set_font_size ( theme_cache . font_size ) ;
2022-01-20 07:30:42 +00:00
text . invalidate_font ( ) ;
2022-01-23 20:07:35 +00:00
_update_placeholder ( ) ;
2018-04-02 11:41:44 +00:00
2021-07-10 10:41:38 +00:00
/* Syntax highlighting. */
2020-03-07 11:17:18 +00:00
if ( syntax_highlighter . is_valid ( ) ) {
2020-05-03 16:08:15 +00:00
syntax_highlighter - > set_text_edit ( this ) ;
2018-04-02 11:41:44 +00:00
}
}
2024-01-22 17:34:12 +00:00
void TextEdit : : _close_ime_window ( ) {
2024-06-03 21:03:02 +00:00
DisplayServer : : WindowID wid = get_window ( ) ? get_window ( ) - > get_window_id ( ) : DisplayServer : : INVALID_WINDOW_ID ;
if ( wid = = DisplayServer : : INVALID_WINDOW_ID | | ! DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_IME ) ) {
2024-01-22 17:34:12 +00:00
return ;
}
2024-06-03 21:03:02 +00:00
DisplayServer : : get_singleton ( ) - > window_set_ime_position ( Point2 ( ) , wid ) ;
DisplayServer : : get_singleton ( ) - > window_set_ime_active ( false , wid ) ;
2024-01-22 17:34:12 +00:00
}
void TextEdit : : _update_ime_window_position ( ) {
2024-06-03 21:03:02 +00:00
DisplayServer : : WindowID wid = get_window ( ) ? get_window ( ) - > get_window_id ( ) : DisplayServer : : INVALID_WINDOW_ID ;
if ( wid = = DisplayServer : : INVALID_WINDOW_ID | | ! DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_IME ) ) {
2024-01-22 17:34:12 +00:00
return ;
}
2024-06-03 21:03:02 +00:00
DisplayServer : : get_singleton ( ) - > window_set_ime_active ( true , wid ) ;
2024-01-22 17:34:12 +00:00
Point2 pos = get_global_position ( ) + get_caret_draw_pos ( ) ;
if ( get_window ( ) - > get_embedder ( ) ) {
pos + = get_viewport ( ) - > get_popup_base_transform ( ) . get_origin ( ) ;
}
// The window will move to the updated position the next time the IME is updated, not immediately.
2024-06-03 21:03:02 +00:00
DisplayServer : : get_singleton ( ) - > window_set_ime_position ( pos , wid ) ;
2024-01-22 17:34:12 +00:00
}
void TextEdit : : _update_ime_text ( ) {
if ( has_ime_text ( ) ) {
// Update text to visually include IME text.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
String text_with_ime = text [ get_caret_line ( i ) ] . substr ( 0 , get_caret_column ( i ) ) + ime_text + text [ get_caret_line ( i ) ] . substr ( get_caret_column ( i ) , text [ get_caret_line ( i ) ] . length ( ) ) ;
text . invalidate_cache ( get_caret_line ( i ) , get_caret_column ( i ) , true , text_with_ime , structured_text_parser ( st_parser , st_args , text_with_ime ) ) ;
}
} else {
// Reset text.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
text . invalidate_cache ( get_caret_line ( i ) , get_caret_column ( i ) , true ) ;
}
}
queue_redraw ( ) ;
}
2021-07-10 10:41:38 +00:00
/* General overrides. */
Size2 TextEdit : : get_minimum_size ( ) const {
2023-04-03 16:01:10 +00:00
Size2 size = theme_cache . style_normal - > get_minimum_size ( ) ;
2022-06-20 23:19:54 +00:00
if ( fit_content_height ) {
size . y + = content_height_cache ;
}
return size ;
2021-07-09 13:05:52 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_text_field ( ) const {
return true ;
2021-07-09 13:05:52 +00:00
}
2021-11-24 21:22:40 +00:00
Variant TextEdit : : get_drag_data ( const Point2 & p_point ) {
2023-03-19 23:38:00 +00:00
Variant ret = Control : : get_drag_data ( p_point ) ;
if ( ret ! = Variant ( ) ) {
return ret ;
}
2022-04-29 15:23:32 +00:00
if ( has_selection ( ) & & selection_drag_attempt ) {
2021-11-24 21:22:40 +00:00
String t = get_selected_text ( ) ;
Label * l = memnew ( Label ) ;
l - > set_text ( t ) ;
set_drag_preview ( l ) ;
return t ;
}
return Variant ( ) ;
}
bool TextEdit : : can_drop_data ( const Point2 & p_point , const Variant & p_data ) const {
bool drop_override = Control : : can_drop_data ( p_point , p_data ) ; // In case user wants to drop custom data.
if ( drop_override ) {
return drop_override ;
}
return is_editable ( ) & & p_data . get_type ( ) = = Variant : : STRING ;
}
void TextEdit : : drop_data ( const Point2 & p_point , const Variant & p_data ) {
Control : : drop_data ( p_point , p_data ) ;
if ( p_data . get_type ( ) = = Variant : : STRING & & is_editable ( ) ) {
Point2i pos = get_line_column_at_pos ( get_local_mouse_pos ( ) ) ;
2024-01-22 23:27:19 +00:00
int drop_at_line = pos . y ;
int drop_at_column = pos . x ;
int selection_index = get_selection_at_line_column ( drop_at_line , drop_at_column , ! Input : : get_singleton ( ) - > is_key_pressed ( Key : : CMD_OR_CTRL ) ) ;
// Remove drag caret before the complex operation starts so it won't appear in undo.
remove_caret ( drag_caret_index ) ;
if ( selection_drag_attempt & & selection_index > = 0 & & selection_index = = drag_and_drop_origin_caret_index ) {
// Dropped onto original selection, do nothing.
selection_drag_attempt = false ;
return ;
}
begin_complex_operation ( ) ;
begin_multicaret_edit ( ) ;
2022-04-29 15:23:32 +00:00
if ( selection_drag_attempt ) {
2024-01-22 23:27:19 +00:00
// Drop from self.
2022-04-29 15:23:32 +00:00
selection_drag_attempt = false ;
2024-01-22 23:27:19 +00:00
if ( ! Input : : get_singleton ( ) - > is_key_pressed ( Key : : CMD_OR_CTRL ) ) {
// Delete all selections.
int temp_caret = add_caret ( drop_at_line , drop_at_column ) ;
2021-11-24 21:22:40 +00:00
2024-01-22 23:27:19 +00:00
delete_selection ( ) ;
2021-11-24 21:22:40 +00:00
2024-01-22 23:27:19 +00:00
// Use a temporary caret to update the drop at position.
drop_at_line = get_caret_line ( temp_caret ) ;
drop_at_column = get_caret_column ( temp_caret ) ;
}
2021-11-24 21:22:40 +00:00
}
2024-01-22 23:27:19 +00:00
remove_secondary_carets ( ) ;
deselect ( ) ;
// Insert the dragged text.
set_caret_line ( drop_at_line , true , false , - 1 ) ;
set_caret_column ( drop_at_column ) ;
insert_text_at_caret ( p_data ) ;
select ( drop_at_line , drop_at_column , get_caret_line ( ) , get_caret_column ( ) ) ;
grab_focus ( ) ;
adjust_viewport_to_caret ( ) ;
end_multicaret_edit ( ) ;
end_complex_operation ( ) ;
2021-11-24 21:22:40 +00:00
}
}
2021-07-10 10:41:38 +00:00
Control : : CursorShape TextEdit : : get_cursor_shape ( const Point2 & p_pos ) const {
Point2i pos = get_line_column_at_pos ( p_pos ) ;
int row = pos . y ;
2021-07-09 12:40:05 +00:00
2023-04-03 16:01:10 +00:00
int left_margin = theme_cache . style_normal - > get_margin ( SIDE_LEFT ) ;
2021-07-10 10:41:38 +00:00
int gutter = left_margin + gutters_width ;
if ( p_pos . x < gutter ) {
for ( int i = 0 ; i < gutters . size ( ) ; i + + ) {
if ( ! gutters [ i ] . draw ) {
continue ;
}
2021-07-08 15:48:58 +00:00
2022-09-14 22:54:14 +00:00
if ( p_pos . x > = left_margin & & p_pos . x < left_margin + gutters [ i ] . width ) {
2021-07-10 10:41:38 +00:00
if ( gutters [ i ] . clickable | | is_line_gutter_clickable ( row , i ) ) {
return CURSOR_POINTING_HAND ;
}
}
left_margin + = gutters [ i ] . width ;
}
return CURSOR_ARROW ;
2021-07-08 15:48:58 +00:00
}
2023-04-03 16:01:10 +00:00
int xmargin_end = get_size ( ) . width - theme_cache . style_normal - > get_margin ( SIDE_RIGHT ) ;
2021-07-10 10:41:38 +00:00
if ( draw_minimap & & p_pos . x > xmargin_end - minimap_width & & p_pos . x < = xmargin_end ) {
return CURSOR_ARROW ;
2021-07-08 15:48:58 +00:00
}
2021-07-10 10:41:38 +00:00
return get_default_cursor_shape ( ) ;
2021-07-08 15:48:58 +00:00
}
2021-07-10 10:41:38 +00:00
String TextEdit : : get_tooltip ( const Point2 & p_pos ) const {
2021-11-07 17:26:15 +00:00
if ( ! tooltip_callback . is_valid ( ) ) {
2021-07-10 10:41:38 +00:00
return Control : : get_tooltip ( p_pos ) ;
2021-07-08 15:48:58 +00:00
}
2021-07-10 10:41:38 +00:00
Point2i pos = get_line_column_at_pos ( p_pos ) ;
int row = pos . y ;
int col = pos . x ;
2021-07-08 15:48:58 +00:00
2021-07-10 10:41:38 +00:00
String s = text [ row ] ;
if ( s . length ( ) = = 0 ) {
return Control : : get_tooltip ( p_pos ) ;
2021-07-08 15:48:58 +00:00
}
2021-07-10 10:41:38 +00:00
int beg , end ;
if ( select_word ( s , col , beg , end ) ) {
2021-11-07 17:26:15 +00:00
Variant args [ 1 ] = { s . substr ( beg , end - beg ) } ;
const Variant * argp [ ] = { & args [ 0 ] } ;
Callable : : CallError ce ;
Variant ret ;
2022-07-28 20:56:41 +00:00
tooltip_callback . callp ( argp , 1 , ret , ce ) ;
2021-11-07 17:26:15 +00:00
ERR_FAIL_COND_V_MSG ( ce . error ! = Callable : : CallError : : CALL_OK , " " , " Failed to call custom tooltip. " ) ;
return ret ;
2021-07-08 15:48:58 +00:00
}
2021-07-10 10:41:38 +00:00
return Control : : get_tooltip ( p_pos ) ;
2021-07-08 17:35:56 +00:00
}
2021-11-07 17:26:15 +00:00
void TextEdit : : set_tooltip_request_func ( const Callable & p_tooltip_callback ) {
tooltip_callback = p_tooltip_callback ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
/* Text */
// Text properties.
bool TextEdit : : has_ime_text ( ) const {
return ! ime_text . is_empty ( ) ;
}
2021-07-08 17:35:56 +00:00
2024-01-22 17:34:12 +00:00
void TextEdit : : cancel_ime ( ) {
if ( ! has_ime_text ( ) ) {
return ;
}
ime_text = String ( ) ;
ime_selection = Point2 ( ) ;
_close_ime_window ( ) ;
_update_ime_text ( ) ;
}
void TextEdit : : apply_ime ( ) {
if ( ! has_ime_text ( ) ) {
return ;
}
// Force apply the current IME text.
String insert_ime_text = ime_text ;
cancel_ime ( ) ;
insert_text_at_caret ( insert_ime_text ) ;
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_editable ( const bool p_editable ) {
if ( editable = = p_editable ) {
return ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
editable = p_editable ;
2021-07-08 17:35:56 +00:00
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_editable ( ) const {
return editable ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_text_direction ( Control : : TextDirection p_text_direction ) {
ERR_FAIL_COND ( ( int ) p_text_direction < - 1 | | ( int ) p_text_direction > 3 ) ;
if ( text_direction ! = p_text_direction ) {
text_direction = p_text_direction ;
if ( text_direction ! = TEXT_DIRECTION_AUTO & & text_direction ! = TEXT_DIRECTION_INHERITED ) {
input_direction = text_direction ;
}
TextServer : : Direction dir ;
if ( text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
dir = is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ;
} else {
dir = ( TextServer : : Direction ) text_direction ;
}
2021-12-09 09:42:46 +00:00
text . set_direction_and_language ( dir , ( ! language . is_empty ( ) ) ? language : TranslationServer : : get_singleton ( ) - > get_tool_locale ( ) ) ;
2022-01-20 07:30:42 +00:00
text . invalidate_font ( ) ;
2022-01-23 20:07:35 +00:00
_update_placeholder ( ) ;
2021-07-08 17:35:56 +00:00
2021-07-10 10:41:38 +00:00
if ( menu_dir ) {
menu_dir - > set_item_checked ( menu_dir - > get_item_index ( MENU_DIR_INHERITED ) , text_direction = = TEXT_DIRECTION_INHERITED ) ;
menu_dir - > set_item_checked ( menu_dir - > get_item_index ( MENU_DIR_AUTO ) , text_direction = = TEXT_DIRECTION_AUTO ) ;
menu_dir - > set_item_checked ( menu_dir - > get_item_index ( MENU_DIR_LTR ) , text_direction = = TEXT_DIRECTION_LTR ) ;
menu_dir - > set_item_checked ( menu_dir - > get_item_index ( MENU_DIR_RTL ) , text_direction = = TEXT_DIRECTION_RTL ) ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
Control : : TextDirection TextEdit : : get_text_direction ( ) const {
return text_direction ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_language ( const String & p_language ) {
if ( language ! = p_language ) {
language = p_language ;
TextServer : : Direction dir ;
if ( text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
dir = is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ;
} else {
dir = ( TextServer : : Direction ) text_direction ;
2021-07-08 17:35:56 +00:00
}
2021-12-09 09:42:46 +00:00
text . set_direction_and_language ( dir , ( ! language . is_empty ( ) ) ? language : TranslationServer : : get_singleton ( ) - > get_tool_locale ( ) ) ;
2021-07-10 10:41:38 +00:00
text . invalidate_all ( ) ;
2022-01-23 20:07:35 +00:00
_update_placeholder ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
}
2021-07-08 17:35:56 +00:00
2021-07-10 10:41:38 +00:00
String TextEdit : : get_language ( ) const {
return language ;
}
2021-07-08 17:35:56 +00:00
2022-04-19 10:27:18 +00:00
void TextEdit : : set_structured_text_bidi_override ( TextServer : : StructuredTextParser p_parser ) {
2021-07-10 10:41:38 +00:00
if ( st_parser ! = p_parser ) {
st_parser = p_parser ;
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
text . set ( i , text [ i ] , structured_text_parser ( st_parser , st_args , text [ i ] ) ) ;
2021-07-08 17:35:56 +00:00
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-08 17:35:56 +00:00
}
}
2022-04-19 10:27:18 +00:00
TextServer : : StructuredTextParser TextEdit : : get_structured_text_bidi_override ( ) const {
2021-07-10 10:41:38 +00:00
return st_parser ;
}
2021-07-08 17:35:56 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_structured_text_bidi_override_options ( Array p_args ) {
2022-03-16 07:50:48 +00:00
if ( st_args = = p_args ) {
return ;
}
2021-07-10 10:41:38 +00:00
st_args = p_args ;
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
text . set ( i , text [ i ] , structured_text_parser ( st_parser , st_args , text [ i ] ) ) ;
2021-07-08 17:35:56 +00:00
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2021-07-08 17:35:56 +00:00
2021-07-10 10:41:38 +00:00
Array TextEdit : : get_structured_text_bidi_override_options ( ) const {
return st_args ;
}
2021-07-08 17:35:56 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_tab_size ( const int p_size ) {
ERR_FAIL_COND_MSG ( p_size < = 0 , " Tab size must be greater than 0. " ) ;
if ( p_size = = text . get_tab_size ( ) ) {
return ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
text . set_tab_size ( p_size ) ;
text . invalidate_all_lines ( ) ;
2022-01-23 20:07:35 +00:00
_update_placeholder ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2021-07-08 17:35:56 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_tab_size ( ) const {
return text . get_tab_size ( ) ;
2021-07-08 17:35:56 +00:00
}
2024-02-19 11:41:12 +00:00
void TextEdit : : set_indent_wrapped_lines ( bool p_enabled ) {
if ( text . is_indent_wrapped_lines ( ) = = p_enabled ) {
return ;
}
text . set_indent_wrapped_lines ( p_enabled ) ;
text . invalidate_all_lines ( ) ;
_update_placeholder ( ) ;
queue_redraw ( ) ;
}
bool TextEdit : : is_indent_wrapped_lines ( ) const {
return text . is_indent_wrapped_lines ( ) ;
}
2021-07-10 10:41:38 +00:00
// User controls
void TextEdit : : set_overtype_mode_enabled ( const bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( overtype_mode = = p_enabled ) {
return ;
}
2021-07-10 10:41:38 +00:00
overtype_mode = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_overtype_mode_enabled ( ) const {
return overtype_mode ;
2021-07-09 10:52:49 +00:00
}
2021-10-08 21:26:13 +00:00
void TextEdit : : set_context_menu_enabled ( bool p_enabled ) {
context_menu_enabled = p_enabled ;
2021-07-10 10:41:38 +00:00
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_context_menu_enabled ( ) const {
return context_menu_enabled ;
}
2021-07-09 10:52:49 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_shortcut_keys_enabled ( bool p_enabled ) {
shortcut_keys_enabled = p_enabled ;
2021-07-09 10:52:49 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_shortcut_keys_enabled ( ) const {
return shortcut_keys_enabled ;
2021-07-09 10:52:49 +00:00
}
2021-10-08 21:26:13 +00:00
void TextEdit : : set_virtual_keyboard_enabled ( bool p_enabled ) {
virtual_keyboard_enabled = p_enabled ;
2021-07-09 10:52:49 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_virtual_keyboard_enabled ( ) const {
return virtual_keyboard_enabled ;
2021-07-09 11:42:55 +00:00
}
2021-07-09 10:52:49 +00:00
2021-10-12 07:43:50 +00:00
void TextEdit : : set_middle_mouse_paste_enabled ( bool p_enabled ) {
middle_mouse_paste_enabled = p_enabled ;
}
bool TextEdit : : is_middle_mouse_paste_enabled ( ) const {
return middle_mouse_paste_enabled ;
}
2021-07-10 10:41:38 +00:00
// Text manipulation
void TextEdit : : clear ( ) {
setting_text = true ;
_clear ( ) ;
setting_text = false ;
2021-07-11 16:07:29 +00:00
emit_signal ( SNAME ( " text_set " ) ) ;
2021-07-09 10:52:49 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : _clear ( ) {
2022-01-12 11:42:38 +00:00
if ( editable & & undo_enabled ) {
2022-06-03 16:47:37 +00:00
remove_secondary_carets ( ) ;
2022-01-12 11:42:38 +00:00
_move_caret_document_start ( false ) ;
begin_complex_operation ( ) ;
_remove_text ( 0 , 0 , MAX ( 0 , get_line_count ( ) - 1 ) , MAX ( get_line ( MAX ( get_line_count ( ) - 1 , 0 ) ) . size ( ) - 1 , 0 ) ) ;
insert_text_at_caret ( " " ) ;
text . clear ( ) ;
end_complex_operation ( ) ;
return ;
}
2022-02-28 18:50:12 +00:00
// Cannot merge with above, as we are not part of the tree on creation.
int old_text_size = text . size ( ) ;
2021-07-10 10:41:38 +00:00
clear_undo_history ( ) ;
text . clear ( ) ;
2022-06-03 16:47:37 +00:00
remove_secondary_carets ( ) ;
2024-01-22 23:26:16 +00:00
set_caret_line ( 0 , false , true , - 1 ) ;
2022-02-28 18:50:12 +00:00
set_caret_column ( 0 ) ;
2022-04-29 15:23:32 +00:00
first_visible_col = 0 ;
first_visible_line = 0 ;
first_visible_line_wrap_ofs = 0 ;
2022-06-03 16:47:37 +00:00
carets . write [ 0 ] . last_fit_x = 0 ;
deselect ( ) ;
2022-02-28 18:50:12 +00:00
emit_signal ( SNAME ( " lines_edited_from " ) , old_text_size , 0 ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-09 10:52:49 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_text ( const String & p_text ) {
setting_text = true ;
if ( ! undo_enabled ) {
_clear ( ) ;
insert_text_at_caret ( p_text ) ;
2021-07-09 10:52:49 +00:00
}
2021-07-10 10:41:38 +00:00
if ( undo_enabled ) {
2022-06-03 16:47:37 +00:00
remove_secondary_carets ( ) ;
2021-07-10 10:41:38 +00:00
set_caret_line ( 0 ) ;
set_caret_column ( 0 ) ;
begin_complex_operation ( ) ;
2021-09-06 16:10:39 +00:00
deselect ( ) ;
2021-07-10 10:41:38 +00:00
_remove_text ( 0 , 0 , MAX ( 0 , get_line_count ( ) - 1 ) , MAX ( get_line ( MAX ( get_line_count ( ) - 1 , 0 ) ) . size ( ) - 1 , 0 ) ) ;
insert_text_at_caret ( p_text ) ;
end_complex_operation ( ) ;
2021-07-09 10:52:49 +00:00
}
2021-07-10 10:41:38 +00:00
set_caret_line ( 0 ) ;
set_caret_column ( 0 ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
setting_text = false ;
2021-07-11 16:07:29 +00:00
emit_signal ( SNAME ( " text_set " ) ) ;
2021-07-08 17:35:56 +00:00
}
2021-07-10 10:41:38 +00:00
String TextEdit : : get_text ( ) const {
2021-08-27 19:05:05 +00:00
StringBuilder ret_text ;
const int text_size = text . size ( ) ;
for ( int i = 0 ; i < text_size ; i + + ) {
ret_text + = text [ i ] ;
if ( i ! = text_size - 1 ) {
ret_text + = " \n " ;
2021-07-10 10:41:38 +00:00
}
2021-07-09 11:42:55 +00:00
}
2021-08-27 19:05:05 +00:00
return ret_text . as_string ( ) ;
2021-07-10 10:41:38 +00:00
}
2018-04-02 11:41:44 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_line_count ( ) const {
return text . size ( ) ;
}
2022-01-23 20:07:35 +00:00
void TextEdit : : set_placeholder ( const String & p_text ) {
2022-03-16 07:50:48 +00:00
if ( placeholder_text = = p_text ) {
return ;
}
2022-01-23 20:07:35 +00:00
placeholder_text = p_text ;
_update_placeholder ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2022-01-23 20:07:35 +00:00
}
String TextEdit : : get_placeholder ( ) const {
return placeholder_text ;
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line ( int p_line , const String & p_new_text ) {
if ( p_line < 0 | | p_line > = text . size ( ) ) {
2021-07-09 11:42:55 +00:00
return ;
2018-04-02 11:41:44 +00:00
}
2022-02-28 18:50:12 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
int old_column = text [ p_line ] . length ( ) ;
// Set the affected carets column to update their last offset x.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( _is_line_col_in_range ( get_caret_line ( i ) , get_caret_column ( i ) , p_line , 0 , p_line , old_column ) ) {
set_caret_column ( get_caret_column ( i ) , false , i ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
if ( has_selection ( i ) & & _is_line_col_in_range ( get_selection_origin_line ( i ) , get_selection_origin_column ( i ) , p_line , 0 , p_line , old_column ) ) {
set_selection_origin_column ( get_selection_origin_column ( i ) , i ) ;
}
}
_remove_text ( p_line , 0 , p_line , old_column ) ;
int new_line , new_column ;
_insert_text ( p_line , 0 , p_new_text , & new_line , & new_column ) ;
// Don't offset carets that were on the old line.
_offset_carets_after ( p_line , old_column , new_line , new_column , false , false ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
// Set the caret lines to update the column to match visually.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( _is_line_col_in_range ( get_caret_line ( i ) , get_caret_column ( i ) , p_line , 0 , p_line , old_column ) ) {
set_caret_line ( get_caret_line ( i ) , false , true , 0 , i ) ;
}
if ( has_selection ( i ) & & _is_line_col_in_range ( get_selection_origin_line ( i ) , get_selection_origin_column ( i ) , p_line , 0 , p_line , old_column ) ) {
set_selection_origin_line ( get_selection_origin_line ( i ) , true , 0 , i ) ;
2022-06-03 16:47:37 +00:00
}
2021-07-10 10:41:38 +00:00
}
2024-01-22 23:27:19 +00:00
merge_overlapping_carets ( ) ;
2022-02-28 18:50:12 +00:00
end_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
String TextEdit : : get_line ( int p_line ) const {
if ( p_line < 0 | | p_line > = text . size ( ) ) {
return " " ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
return text [ p_line ] ;
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_line_width ( int p_line , int p_wrap_index ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
ERR_FAIL_COND_V ( p_wrap_index > get_line_wrap_count ( p_line ) , 0 ) ;
return text . get_line_width ( p_line , p_wrap_index ) ;
}
int TextEdit : : get_line_height ( ) const {
2023-04-30 02:54:50 +00:00
return MAX ( text . get_line_height ( ) + theme_cache . line_spacing , 1 ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
int TextEdit : : get_indent_level ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
int tab_count = 0 ;
int whitespace_count = 0 ;
int line_length = text [ p_line ] . size ( ) ;
for ( int i = 0 ; i < line_length - 1 ; i + + ) {
if ( text [ p_line ] [ i ] = = ' \t ' ) {
tab_count + + ;
} else if ( text [ p_line ] [ i ] = = ' ' ) {
whitespace_count + + ;
} else {
break ;
}
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
return tab_count * text . get_tab_size ( ) + whitespace_count ;
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_first_non_whitespace_column ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
int col = 0 ;
2022-02-04 08:32:20 +00:00
while ( col < text [ p_line ] . length ( ) & & is_whitespace ( text [ p_line ] [ col ] ) ) {
2021-07-10 10:41:38 +00:00
col + + ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
return col ;
}
void TextEdit : : swap_lines ( int p_from_line , int p_to_line ) {
ERR_FAIL_INDEX ( p_from_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_to_line , text . size ( ) ) ;
2024-01-22 23:27:19 +00:00
if ( p_from_line = = p_to_line ) {
return ;
}
String from_line_text = get_line ( p_from_line ) ;
String to_line_text = get_line ( p_to_line ) ;
begin_complex_operation ( ) ;
begin_multicaret_edit ( ) ;
// Don't use set_line to avoid clamping and updating carets.
_remove_text ( p_to_line , 0 , p_to_line , text [ p_to_line ] . length ( ) ) ;
_insert_text ( p_to_line , 0 , from_line_text ) ;
_remove_text ( p_from_line , 0 , p_from_line , text [ p_from_line ] . length ( ) ) ;
_insert_text ( p_from_line , 0 , to_line_text ) ;
// Swap carets.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
bool selected = has_selection ( i ) ;
if ( get_caret_line ( i ) = = p_from_line | | get_caret_line ( i ) = = p_to_line ) {
int caret_new_line = get_caret_line ( i ) = = p_from_line ? p_to_line : p_from_line ;
int caret_column = get_caret_column ( i ) ;
set_caret_line ( caret_new_line , false , true , - 1 , i ) ;
set_caret_column ( caret_column , false , i ) ;
}
if ( selected & & ( get_selection_origin_line ( i ) = = p_from_line | | get_selection_origin_line ( i ) = = p_to_line ) ) {
int origin_new_line = get_selection_origin_line ( i ) = = p_from_line ? p_to_line : p_from_line ;
int origin_column = get_selection_origin_column ( i ) ;
select ( origin_new_line , origin_column , get_caret_line ( i ) , get_caret_column ( i ) , i ) ;
}
}
// If only part of a selection was changed, it may now overlap.
merge_overlapping_carets ( ) ;
end_multicaret_edit ( ) ;
end_complex_operation ( ) ;
}
void TextEdit : : insert_line_at ( int p_line , const String & p_text ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
// Use a complex operation so subsequent calls aren't merged together.
2022-02-28 18:50:12 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
int new_line , new_column ;
_insert_text ( p_line , 0 , p_text + " \n " , & new_line , & new_column ) ;
_offset_carets_after ( p_line , 0 , new_line , new_column ) ;
2022-02-28 18:50:12 +00:00
end_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
}
2024-01-22 23:27:19 +00:00
void TextEdit : : remove_line_at ( int p_line , bool p_move_carets_down ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
if ( get_line_count ( ) = = 1 ) {
// Only one line, just remove contents.
begin_complex_operation ( ) ;
int line_length = get_line ( p_line ) . length ( ) ;
_remove_text ( p_line , 0 , p_line , line_length ) ;
collapse_carets ( p_line , 0 , p_line , line_length , true ) ;
end_complex_operation ( ) ;
return ;
}
begin_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
2024-01-22 23:27:19 +00:00
bool is_last_line = p_line = = get_line_count ( ) - 1 ;
int from_line = is_last_line ? p_line - 1 : p_line ;
int next_line = is_last_line ? p_line : p_line + 1 ;
int from_column = is_last_line ? get_line ( from_line ) . length ( ) : 0 ;
int next_column = is_last_line ? get_line ( next_line ) . length ( ) : 0 ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
if ( ( ! is_last_line & & p_move_carets_down ) | | ( p_line ! = 0 & & ! p_move_carets_down ) ) {
// Set the carets column to update their last offset x.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( get_caret_line ( i ) = = p_line ) {
set_caret_column ( get_caret_column ( i ) , false , i ) ;
}
if ( has_selection ( i ) & & get_selection_origin_line ( i ) = = p_line ) {
set_selection_origin_column ( get_selection_origin_column ( i ) , i ) ;
2022-06-03 16:47:37 +00:00
}
2021-07-10 10:41:38 +00:00
}
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
// Remove line.
_remove_text ( from_line , from_column , next_line , next_column ) ;
begin_multicaret_edit ( ) ;
if ( ( is_last_line & & p_move_carets_down ) | | ( p_line = = 0 & & ! p_move_carets_down ) ) {
// Collapse carets.
collapse_carets ( from_line , from_column , next_line , next_column , true ) ;
} else {
// Move carets to visually line up.
int target_line = p_move_carets_down ? p_line : p_line - 1 ;
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
bool selected = has_selection ( i ) ;
if ( get_caret_line ( i ) = = p_line ) {
set_caret_line ( target_line , i = = 0 , true , 0 , i ) ;
}
if ( selected & & get_selection_origin_line ( i ) = = p_line ) {
set_selection_origin_line ( target_line , true , 0 , i ) ;
select ( get_selection_origin_line ( i ) , get_selection_origin_column ( i ) , get_caret_line ( i ) , get_caret_column ( i ) , i ) ;
}
}
merge_overlapping_carets ( ) ;
}
_offset_carets_after ( next_line , next_column , from_line , from_column ) ;
end_multicaret_edit ( ) ;
end_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
}
2021-07-09 11:42:55 +00:00
2022-06-03 16:47:37 +00:00
void TextEdit : : insert_text_at_caret ( const String & p_text , int p_caret ) {
2024-01-22 23:27:19 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2021-08-28 11:23:51 +00:00
2022-06-03 16:47:37 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_caret ! = - 1 & & p_caret ! = i ) {
continue ;
}
2024-01-22 23:27:19 +00:00
if ( p_caret = = - 1 & & multicaret_edit_ignore_caret ( i ) ) {
continue ;
}
2021-07-10 10:41:38 +00:00
2022-06-03 16:47:37 +00:00
delete_selection ( i ) ;
2021-07-10 10:41:38 +00:00
2022-06-03 16:47:37 +00:00
int from_line = get_caret_line ( i ) ;
int from_col = get_caret_column ( i ) ;
2021-08-28 11:23:51 +00:00
2024-01-22 23:27:19 +00:00
int new_line , new_column ;
2022-06-03 16:47:37 +00:00
_insert_text ( from_line , from_col , p_text , & new_line , & new_column ) ;
_update_scrollbars ( ) ;
2024-01-22 23:27:19 +00:00
_offset_carets_after ( from_line , from_col , new_line , new_column ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
set_caret_line ( new_line , false , true , - 1 , i ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( new_column , i = = 0 , i ) ;
2021-09-29 16:25:53 +00:00
}
2023-01-26 07:09:53 +00:00
2024-01-22 17:34:12 +00:00
if ( has_ime_text ( ) ) {
_update_ime_text ( ) ;
2023-01-26 07:09:53 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
end_complex_operation ( ) ;
}
void TextEdit : : insert_text ( const String & p_text , int p_line , int p_column , bool p_before_selection_begin , bool p_before_selection_end ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_column , text [ p_line ] . length ( ) + 1 ) ;
begin_complex_operation ( ) ;
int new_line , new_column ;
_insert_text ( p_line , p_column , p_text , & new_line , & new_column ) ;
_offset_carets_after ( p_line , p_column , new_line , new_column , p_before_selection_begin , p_before_selection_end ) ;
2022-06-03 16:47:37 +00:00
end_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
}
void TextEdit : : remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
ERR_FAIL_INDEX ( p_from_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_from_column , text [ p_from_line ] . length ( ) + 1 ) ;
ERR_FAIL_INDEX ( p_to_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_to_column , text [ p_to_line ] . length ( ) + 1 ) ;
ERR_FAIL_COND ( p_to_line < p_from_line ) ;
ERR_FAIL_COND ( p_to_line = = p_from_line & & p_to_column < p_from_column ) ;
2024-01-22 23:27:19 +00:00
begin_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
_remove_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
2024-01-22 23:27:19 +00:00
collapse_carets ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
_offset_carets_after ( p_to_line , p_to_column , p_from_line , p_from_column ) ;
end_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
}
int TextEdit : : get_last_unhidden_line ( ) const {
// Returns the last line in the text that is not hidden.
if ( ! _is_hiding_enabled ( ) ) {
return text . size ( ) - 1 ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
int last_line ;
for ( last_line = text . size ( ) - 1 ; last_line > 0 ; last_line - - ) {
if ( ! _is_line_hidden ( last_line ) ) {
break ;
}
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
return last_line ;
}
int TextEdit : : get_next_visible_line_offset_from ( int p_line_from , int p_visible_amount ) const {
// Returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines).
ERR_FAIL_INDEX_V ( p_line_from , text . size ( ) , ABS ( p_visible_amount ) ) ;
if ( ! _is_hiding_enabled ( ) ) {
return ABS ( p_visible_amount ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
int num_visible = 0 ;
int num_total = 0 ;
if ( p_visible_amount > = 0 ) {
for ( int i = p_line_from ; i < text . size ( ) ; i + + ) {
num_total + + ;
if ( ! _is_line_hidden ( i ) ) {
num_visible + + ;
}
if ( num_visible > = p_visible_amount ) {
break ;
}
}
} else {
p_visible_amount = ABS ( p_visible_amount ) ;
for ( int i = p_line_from ; i > = 0 ; i - - ) {
num_total + + ;
if ( ! _is_line_hidden ( i ) ) {
num_visible + + ;
}
if ( num_visible > = p_visible_amount ) {
break ;
}
}
}
return num_total ;
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
Point2i TextEdit : : get_next_visible_line_index_offset_from ( int p_line_from , int p_wrap_index_from , int p_visible_amount ) const {
// Returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows).
// Wrap index is set to the wrap index of the last line.
int wrap_index = 0 ;
ERR_FAIL_INDEX_V ( p_line_from , text . size ( ) , Point2i ( ABS ( p_visible_amount ) , 0 ) ) ;
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
if ( ! _is_hiding_enabled ( ) & & get_line_wrapping_mode ( ) = = LineWrappingMode : : LINE_WRAPPING_NONE ) {
return Point2i ( ABS ( p_visible_amount ) , 0 ) ;
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
int num_visible = 0 ;
int num_total = 0 ;
if ( p_visible_amount = = 0 ) {
num_total = 0 ;
wrap_index = 0 ;
} else if ( p_visible_amount > 0 ) {
int i ;
num_visible - = p_wrap_index_from ;
for ( i = p_line_from ; i < text . size ( ) ; i + + ) {
num_total + + ;
if ( ! _is_line_hidden ( i ) ) {
num_visible + + ;
num_visible + = get_line_wrap_count ( i ) ;
}
if ( num_visible > = p_visible_amount ) {
break ;
}
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
wrap_index = get_line_wrap_count ( MIN ( i , text . size ( ) - 1 ) ) - MAX ( 0 , num_visible - p_visible_amount ) ;
2021-10-31 18:34:54 +00:00
// If we are a hidden line, then we are the last line as we cannot reach "p_visible_amount".
// This means we need to backtrack to get last visible line.
// Currently, line 0 cannot be hidden so this should always be valid.
int line = ( p_line_from + num_total ) - 1 ;
if ( _is_line_hidden ( line ) ) {
Point2i backtrack = get_next_visible_line_index_offset_from ( line , 0 , - 1 ) ;
num_total = num_total - ( backtrack . x - 1 ) ;
wrap_index = backtrack . y ;
}
2021-07-09 11:42:55 +00:00
} else {
2021-07-10 10:41:38 +00:00
p_visible_amount = ABS ( p_visible_amount ) ;
int i ;
num_visible - = get_line_wrap_count ( p_line_from ) - p_wrap_index_from ;
for ( i = p_line_from ; i > = 0 ; i - - ) {
num_total + + ;
if ( ! _is_line_hidden ( i ) ) {
num_visible + + ;
num_visible + = get_line_wrap_count ( i ) ;
}
if ( num_visible > = p_visible_amount ) {
break ;
}
}
wrap_index = MAX ( 0 , num_visible - p_visible_amount ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
wrap_index = MAX ( wrap_index , 0 ) ;
return Point2i ( num_total , wrap_index ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
// Overridable actions
2022-06-03 16:47:37 +00:00
void TextEdit : : handle_unicode_input ( const uint32_t p_unicode , int p_caret ) {
if ( GDVIRTUAL_CALL ( _handle_unicode_input , p_unicode , p_caret ) ) {
2021-07-10 10:41:38 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
_handle_unicode_input_internal ( p_unicode , p_caret ) ;
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : backspace ( int p_caret ) {
if ( GDVIRTUAL_CALL ( _backspace , p_caret ) ) {
2021-07-10 10:41:38 +00:00
return ;
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
_backspace_internal ( p_caret ) ;
2021-07-10 10:41:38 +00:00
}
2021-07-09 11:42:55 +00:00
2022-06-03 16:47:37 +00:00
void TextEdit : : cut ( int p_caret ) {
if ( GDVIRTUAL_CALL ( _cut , p_caret ) ) {
2021-07-10 10:41:38 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
_cut_internal ( p_caret ) ;
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : copy ( int p_caret ) {
if ( GDVIRTUAL_CALL ( _copy , p_caret ) ) {
2021-07-10 10:41:38 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
_copy_internal ( p_caret ) ;
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : paste ( int p_caret ) {
if ( GDVIRTUAL_CALL ( _paste , p_caret ) ) {
2021-07-10 10:41:38 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
_paste_internal ( p_caret ) ;
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : paste_primary_clipboard ( int p_caret ) {
if ( GDVIRTUAL_CALL ( _paste_primary_clipboard , p_caret ) ) {
2021-10-12 07:43:50 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
_paste_primary_clipboard_internal ( p_caret ) ;
2021-10-12 07:43:50 +00:00
}
2021-07-10 10:41:38 +00:00
// Context menu.
PopupMenu * TextEdit : : get_menu ( ) const {
2023-01-26 17:59:41 +00:00
if ( ! menu ) {
const_cast < TextEdit * > ( this ) - > _generate_context_menu ( ) ;
}
2021-07-10 10:41:38 +00:00
return menu ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_menu_visible ( ) const {
return menu & & menu - > is_visible ( ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : menu_option ( int p_option ) {
switch ( p_option ) {
case MENU_CUT : {
cut ( ) ;
} break ;
case MENU_COPY : {
copy ( ) ;
} break ;
case MENU_PASTE : {
paste ( ) ;
} break ;
case MENU_CLEAR : {
if ( editable ) {
clear ( ) ;
}
} break ;
case MENU_SELECT_ALL : {
select_all ( ) ;
} break ;
case MENU_UNDO : {
undo ( ) ;
} break ;
case MENU_REDO : {
redo ( ) ;
} break ;
case MENU_DIR_INHERITED : {
set_text_direction ( TEXT_DIRECTION_INHERITED ) ;
} break ;
case MENU_DIR_AUTO : {
set_text_direction ( TEXT_DIRECTION_AUTO ) ;
} break ;
case MENU_DIR_LTR : {
set_text_direction ( TEXT_DIRECTION_LTR ) ;
} break ;
case MENU_DIR_RTL : {
set_text_direction ( TEXT_DIRECTION_RTL ) ;
} break ;
case MENU_DISPLAY_UCC : {
set_draw_control_chars ( ! get_draw_control_chars ( ) ) ;
} break ;
case MENU_INSERT_LRM : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x200E ) ) ;
}
} break ;
case MENU_INSERT_RLM : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x200F ) ) ;
}
} break ;
case MENU_INSERT_LRE : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x202A ) ) ;
}
} break ;
case MENU_INSERT_RLE : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x202B ) ) ;
}
} break ;
case MENU_INSERT_LRO : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x202D ) ) ;
}
} break ;
case MENU_INSERT_RLO : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x202E ) ) ;
}
} break ;
case MENU_INSERT_PDF : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x202C ) ) ;
}
} break ;
case MENU_INSERT_ALM : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x061C ) ) ;
}
} break ;
case MENU_INSERT_LRI : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x2066 ) ) ;
}
} break ;
case MENU_INSERT_RLI : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x2067 ) ) ;
}
} break ;
case MENU_INSERT_FSI : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x2068 ) ) ;
}
} break ;
case MENU_INSERT_PDI : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x2069 ) ) ;
}
} break ;
case MENU_INSERT_ZWJ : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x200D ) ) ;
}
} break ;
case MENU_INSERT_ZWNJ : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x200C ) ) ;
}
} break ;
case MENU_INSERT_WJ : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x2060 ) ) ;
}
} break ;
case MENU_INSERT_SHY : {
if ( editable ) {
insert_text_at_caret ( String : : chr ( 0x00AD ) ) ;
}
}
}
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
/* Versioning */
2022-06-03 16:47:37 +00:00
void TextEdit : : start_action ( EditAction p_action ) {
if ( current_action ! = p_action ) {
if ( current_action ! = EditAction : : ACTION_NONE ) {
in_action = false ;
pending_action_end = false ;
end_complex_operation ( ) ;
}
if ( p_action ! = EditAction : : ACTION_NONE ) {
in_action = true ;
begin_complex_operation ( ) ;
}
} else if ( current_action ! = EditAction : : ACTION_NONE ) {
pending_action_end = false ;
}
current_action = p_action ;
}
void TextEdit : : end_action ( ) {
if ( current_action ! = EditAction : : ACTION_NONE ) {
pending_action_end = true ;
}
}
TextEdit : : EditAction TextEdit : : get_current_action ( ) const {
return current_action ;
}
2021-07-10 10:41:38 +00:00
void TextEdit : : begin_complex_operation ( ) {
_push_current_op ( ) ;
2021-08-28 11:23:51 +00:00
if ( complex_operation_count = = 0 ) {
next_operation_is_complex = true ;
2022-06-03 16:47:37 +00:00
current_op . start_carets = carets ;
2021-08-28 11:23:51 +00:00
}
complex_operation_count + + ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : end_complex_operation ( ) {
_push_current_op ( ) ;
2021-07-09 11:42:55 +00:00
2021-08-28 11:23:51 +00:00
complex_operation_count = MAX ( complex_operation_count - 1 , 0 ) ;
if ( complex_operation_count > 0 ) {
return ;
}
2024-06-11 16:16:36 +00:00
if ( undo_stack . is_empty ( ) ) {
return ;
}
2022-06-03 16:47:37 +00:00
undo_stack . back ( ) - > get ( ) . end_carets = carets ;
2021-07-10 10:41:38 +00:00
if ( undo_stack . back ( ) - > get ( ) . chain_forward ) {
undo_stack . back ( ) - > get ( ) . chain_forward = false ;
2021-07-09 11:42:55 +00:00
return ;
}
2021-07-10 10:41:38 +00:00
undo_stack . back ( ) - > get ( ) . chain_backward = true ;
2021-07-09 11:42:55 +00:00
}
2021-08-17 03:41:46 +00:00
bool TextEdit : : has_undo ( ) const {
if ( undo_stack_pos = = nullptr ) {
int pending = current_op . type = = TextOperation : : TYPE_NONE ? 0 : 1 ;
return undo_stack . size ( ) + pending > 0 ;
}
return undo_stack_pos ! = undo_stack . front ( ) ;
}
bool TextEdit : : has_redo ( ) const {
return undo_stack_pos ! = nullptr ;
}
2021-07-10 10:41:38 +00:00
void TextEdit : : undo ( ) {
if ( ! editable ) {
return ;
2021-07-09 11:42:55 +00:00
}
2022-10-16 15:28:22 +00:00
if ( in_action ) {
pending_action_end = true ;
}
2021-07-10 10:41:38 +00:00
_push_current_op ( ) ;
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
if ( undo_stack_pos = = nullptr ) {
2024-01-22 23:26:16 +00:00
if ( undo_stack . is_empty ( ) ) {
2021-07-10 10:41:38 +00:00
return ; // Nothing to undo.
}
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
undo_stack_pos = undo_stack . back ( ) ;
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
} else if ( undo_stack_pos = = undo_stack . front ( ) ) {
return ; // At the bottom of the undo stack.
} else {
undo_stack_pos = undo_stack_pos - > prev ( ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-10 10:41:38 +00:00
deselect ( ) ;
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
TextOperation op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , true ) ;
2021-07-09 11:42:55 +00:00
2021-07-10 10:41:38 +00:00
current_op . version = op . prev_version ;
if ( undo_stack_pos - > get ( ) . chain_backward ) {
2024-01-22 23:26:16 +00:00
// This was part of a complex operation, undo until the chain forward at the start of the complex operation.
2021-07-10 10:41:38 +00:00
while ( true ) {
ERR_BREAK ( ! undo_stack_pos - > prev ( ) ) ;
undo_stack_pos = undo_stack_pos - > prev ( ) ;
op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , true ) ;
current_op . version = op . prev_version ;
if ( undo_stack_pos - > get ( ) . chain_forward ) {
break ;
}
2021-07-09 11:42:55 +00:00
}
}
2022-06-03 16:47:37 +00:00
_update_scrollbars ( ) ;
2024-01-22 23:26:16 +00:00
bool dirty_carets = get_caret_count ( ) ! = undo_stack_pos - > get ( ) . start_carets . size ( ) ;
2022-06-03 16:47:37 +00:00
if ( ! dirty_carets ) {
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( carets [ i ] . line ! = undo_stack_pos - > get ( ) . start_carets [ i ] . line | | carets [ i ] . column ! = undo_stack_pos - > get ( ) . start_carets [ i ] . column ) {
dirty_carets = true ;
break ;
}
}
}
carets = undo_stack_pos - > get ( ) . start_carets ;
2022-04-05 01:12:10 +00:00
2024-01-22 23:26:16 +00:00
_unhide_carets ( ) ;
if ( dirty_carets ) {
_caret_changed ( ) ;
_selection_changed ( ) ;
2021-07-09 11:42:55 +00:00
}
2022-10-16 14:06:13 +00:00
adjust_viewport_to_caret ( ) ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : redo ( ) {
if ( ! editable ) {
return ;
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
2022-10-16 15:28:22 +00:00
if ( in_action ) {
pending_action_end = true ;
}
2021-07-10 10:41:38 +00:00
_push_current_op ( ) ;
2020-07-25 00:15:23 +00:00
2021-07-10 10:41:38 +00:00
if ( undo_stack_pos = = nullptr ) {
return ; // Nothing to do.
2020-07-25 00:15:23 +00:00
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
deselect ( ) ;
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
TextOperation op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , false ) ;
current_op . version = op . version ;
if ( undo_stack_pos - > get ( ) . chain_forward ) {
2024-01-22 23:26:16 +00:00
// This was part of a complex operation, redo until the chain backward at the end of the complex operation.
2021-07-10 10:41:38 +00:00
while ( true ) {
ERR_BREAK ( ! undo_stack_pos - > next ( ) ) ;
undo_stack_pos = undo_stack_pos - > next ( ) ;
op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , false ) ;
current_op . version = op . version ;
if ( undo_stack_pos - > get ( ) . chain_backward ) {
break ;
}
2021-07-09 16:27:09 +00:00
}
}
2021-07-10 10:41:38 +00:00
_update_scrollbars ( ) ;
2024-01-22 23:26:16 +00:00
bool dirty_carets = get_caret_count ( ) ! = undo_stack_pos - > get ( ) . end_carets . size ( ) ;
2022-06-03 16:47:37 +00:00
if ( ! dirty_carets ) {
2024-01-22 23:26:16 +00:00
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( carets [ i ] . line ! = undo_stack_pos - > get ( ) . end_carets [ i ] . line | | carets [ i ] . column ! = undo_stack_pos - > get ( ) . end_carets [ i ] . column ) {
dirty_carets = true ;
break ;
}
}
}
carets = undo_stack_pos - > get ( ) . end_carets ;
2021-07-10 10:41:38 +00:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:26:16 +00:00
_unhide_carets ( ) ;
if ( dirty_carets ) {
_caret_changed ( ) ;
_selection_changed ( ) ;
2022-06-03 16:47:37 +00:00
}
2022-10-16 14:06:13 +00:00
adjust_viewport_to_caret ( ) ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : clear_undo_history ( ) {
saved_version = 0 ;
current_op . type = TextOperation : : TYPE_NONE ;
undo_stack_pos = nullptr ;
undo_stack . clear ( ) ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_insert_text_operation ( ) const {
2022-06-03 16:47:37 +00:00
return ( current_op . type = = TextOperation : : TYPE_INSERT | | current_action = = EditAction : : ACTION_TYPING ) ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : tag_saved_version ( ) {
saved_version = get_version ( ) ;
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
uint32_t TextEdit : : get_version ( ) const {
return current_op . version ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
uint32_t TextEdit : : get_saved_version ( ) const {
return saved_version ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
/* Search */
void TextEdit : : set_search_text ( const String & p_search_text ) {
search_text = p_search_text ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_search_flags ( uint32_t p_flags ) {
search_flags = p_flags ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
Point2i TextEdit : : search ( const String & p_key , uint32_t p_search_flags , int p_from_line , int p_from_column ) const {
if ( p_key . length ( ) = = 0 ) {
return Point2 ( - 1 , - 1 ) ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
ERR_FAIL_INDEX_V ( p_from_line , text . size ( ) , Point2i ( - 1 , - 1 ) ) ;
ERR_FAIL_INDEX_V ( p_from_column , text [ p_from_line ] . length ( ) + 1 , Point2i ( - 1 , - 1 ) ) ;
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
// Search through the whole document, but start by current line.
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
int line = p_from_line ;
int pos = - 1 ;
2021-07-09 16:27:09 +00:00
2023-10-02 20:30:23 +00:00
bool key_start_is_symbol = is_symbol ( p_key [ 0 ] ) ;
bool key_end_is_symbol = is_symbol ( p_key [ p_key . length ( ) - 1 ] ) ;
2021-07-10 10:41:38 +00:00
for ( int i = 0 ; i < text . size ( ) + 1 ; i + + ) {
if ( line < 0 ) {
line = text . size ( ) - 1 ;
}
if ( line = = text . size ( ) ) {
line = 0 ;
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
String text_line = text [ line ] ;
int from_column = 0 ;
if ( line = = p_from_line ) {
if ( i = = text . size ( ) ) {
// Wrapped.
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
if ( p_search_flags & SEARCH_BACKWARDS ) {
from_column = text_line . length ( ) ;
} else {
from_column = 0 ;
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
} else {
from_column = p_from_column ;
}
2021-07-09 16:27:09 +00:00
} else {
2021-07-10 10:41:38 +00:00
if ( p_search_flags & SEARCH_BACKWARDS ) {
from_column = text_line . length ( ) - 1 ;
} else {
from_column = 0 ;
}
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
pos = - 1 ;
int pos_from = ( p_search_flags & SEARCH_BACKWARDS ) ? text_line . length ( ) : 0 ;
int last_pos = - 1 ;
while ( true ) {
if ( p_search_flags & SEARCH_BACKWARDS ) {
while ( ( last_pos = ( p_search_flags & SEARCH_MATCH_CASE ) ? text_line . rfind ( p_key , pos_from ) : text_line . rfindn ( p_key , pos_from ) ) ! = - 1 ) {
if ( last_pos < = from_column ) {
pos = last_pos ;
break ;
}
pos_from = last_pos - p_key . length ( ) ;
if ( pos_from < 0 ) {
break ;
}
}
2021-07-09 16:27:09 +00:00
} else {
2021-07-10 10:41:38 +00:00
while ( ( last_pos = ( p_search_flags & SEARCH_MATCH_CASE ) ? text_line . find ( p_key , pos_from ) : text_line . findn ( p_key , pos_from ) ) ! = - 1 ) {
if ( last_pos > = from_column ) {
pos = last_pos ;
break ;
}
pos_from = last_pos + p_key . length ( ) ;
}
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
bool is_match = true ;
if ( pos ! = - 1 & & ( p_search_flags & SEARCH_WHOLE_WORDS ) ) {
// Validate for whole words.
2023-10-02 20:30:23 +00:00
if ( ! key_start_is_symbol & & pos > 0 & & ! is_symbol ( text_line [ pos - 1 ] ) ) {
2021-07-10 10:41:38 +00:00
is_match = false ;
2023-10-02 20:30:23 +00:00
} else if ( ! key_end_is_symbol & & pos + p_key . length ( ) < text_line . length ( ) & & ! is_symbol ( text_line [ pos + p_key . length ( ) ] ) ) {
2021-07-10 10:41:38 +00:00
is_match = false ;
}
}
if ( pos_from = = - 1 ) {
pos = - 1 ;
}
if ( is_match | | last_pos = = - 1 | | pos = = - 1 ) {
break ;
}
pos_from = ( p_search_flags & SEARCH_BACKWARDS ) ? pos - 1 : pos + 1 ;
pos = - 1 ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
if ( pos ! = - 1 ) {
break ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
if ( p_search_flags & SEARCH_BACKWARDS ) {
line - - ;
} else {
line + + ;
2021-07-09 16:27:09 +00:00
}
}
2021-07-10 10:41:38 +00:00
return ( pos = = - 1 ) ? Point2i ( - 1 , - 1 ) : Point2i ( pos , line ) ;
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
/* Mouse */
Point2 TextEdit : : get_local_mouse_pos ( ) const {
Point2 mp = get_local_mouse_position ( ) ;
if ( is_layout_rtl ( ) ) {
mp . x = get_size ( ) . width - mp . x ;
}
return mp ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
String TextEdit : : get_word_at_pos ( const Vector2 & p_pos ) const {
Point2i pos = get_line_column_at_pos ( p_pos ) ;
int row = pos . y ;
int col = pos . x ;
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
String s = text [ row ] ;
if ( s . length ( ) = = 0 ) {
return " " ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
int beg , end ;
if ( select_word ( s , col , beg , end ) ) {
bool inside_quotes = false ;
char32_t selected_quote = ' \0 ' ;
int qbegin = 0 , qend = 0 ;
for ( int i = 0 ; i < s . length ( ) ; i + + ) {
if ( s [ i ] = = ' " ' | | s [ i ] = = ' \' ' ) {
if ( i = = 0 | | s [ i - 1 ] ! = ' \\ ' ) {
if ( inside_quotes & & selected_quote = = s [ i ] ) {
qend = i ;
inside_quotes = false ;
selected_quote = ' \0 ' ;
if ( col > = qbegin & & col < = qend ) {
return s . substr ( qbegin , qend - qbegin ) ;
}
} else if ( ! inside_quotes ) {
qbegin = i + 1 ;
inside_quotes = true ;
selected_quote = s [ i ] ;
}
}
}
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
return s . substr ( beg , end - beg ) ;
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
return String ( ) ;
}
2021-07-09 16:27:09 +00:00
2021-10-31 18:34:54 +00:00
Point2i TextEdit : : get_line_column_at_pos ( const Point2i & p_pos , bool p_allow_out_of_bounds ) const {
2024-05-29 06:29:14 +00:00
float rows = p_pos . y - theme_cache . style_normal - > get_margin ( SIDE_TOP ) ;
if ( ! editable ) {
rows - = theme_cache . style_readonly - > get_offset ( ) . y / 2 ;
rows + = theme_cache . style_normal - > get_offset ( ) . y / 2 ;
}
2021-07-10 10:41:38 +00:00
rows / = get_line_height ( ) ;
rows + = _get_v_scroll_offset ( ) ;
int first_vis_line = get_first_visible_line ( ) ;
int row = first_vis_line + Math : : floor ( rows ) ;
int wrap_index = 0 ;
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
if ( get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE | | _is_hiding_enabled ( ) ) {
2022-04-29 15:23:32 +00:00
Point2i f_ofs = get_next_visible_line_index_offset_from ( first_vis_line , first_visible_line_wrap_ofs , rows + ( 1 * SIGN ( rows ) ) ) ;
2021-07-10 10:41:38 +00:00
wrap_index = f_ofs . y ;
2021-10-31 18:34:54 +00:00
2021-07-10 10:41:38 +00:00
if ( rows < 0 ) {
row = first_vis_line - ( f_ofs . x - 1 ) ;
2021-07-09 16:27:09 +00:00
} else {
2021-07-10 10:41:38 +00:00
row = first_vis_line + ( f_ofs . x - 1 ) ;
2021-07-09 16:27:09 +00:00
}
}
2024-01-22 23:26:16 +00:00
row = CLAMP ( row , 0 , text . size ( ) - 1 ) ;
2021-10-31 18:34:54 +00:00
int visible_lines = get_visible_line_count_in_range ( first_vis_line , row ) ;
if ( rows > visible_lines ) {
if ( ! p_allow_out_of_bounds ) {
return Point2i ( - 1 , - 1 ) ;
2021-07-10 10:41:38 +00:00
}
2022-11-23 13:44:50 +00:00
return Point2i ( text [ row ] . length ( ) , row ) ;
2021-10-31 18:34:54 +00:00
}
2021-07-09 16:27:09 +00:00
2021-10-31 18:34:54 +00:00
int col = 0 ;
2023-04-03 16:01:10 +00:00
int colx = p_pos . x - ( theme_cache . style_normal - > get_margin ( SIDE_LEFT ) + gutters_width + gutter_padding ) ;
2022-04-29 15:23:32 +00:00
colx + = first_visible_col ;
2024-05-29 06:29:14 +00:00
if ( ! editable ) {
colx - = theme_cache . style_readonly - > get_offset ( ) . x / 2 ;
colx + = theme_cache . style_normal - > get_offset ( ) . x / 2 ;
}
2021-10-31 18:34:54 +00:00
col = _get_char_pos_for_line ( colx , row , wrap_index ) ;
if ( get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE & & wrap_index < get_line_wrap_count ( row ) ) {
// Move back one if we are at the end of the row.
Vector < String > rows2 = get_line_wrapped_text ( row ) ;
int row_end_col = 0 ;
for ( int i = 0 ; i < wrap_index + 1 ; i + + ) {
row_end_col + = rows2 [ i ] . length ( ) ;
2021-07-10 10:41:38 +00:00
}
2021-10-31 18:34:54 +00:00
if ( col > = row_end_col ) {
col - = 1 ;
}
}
RID text_rid = text . get_line_data ( row ) - > get_line_rid ( wrap_index ) ;
2024-02-19 11:41:12 +00:00
float wrap_indent = ( text . is_indent_wrapped_lines ( ) & & wrap_index > 0 ) ? get_indent_level ( row ) * theme_cache . font - > get_char_size ( ' ' , theme_cache . font_size ) . width : 0.0 ;
2021-10-31 18:34:54 +00:00
if ( is_layout_rtl ( ) ) {
2024-02-19 11:41:12 +00:00
colx = TS - > shaped_text_get_size ( text_rid ) . x - colx + wrap_indent ;
} else {
colx - = wrap_indent ;
2021-07-09 16:27:09 +00:00
}
2021-10-31 18:34:54 +00:00
col = TS - > shaped_text_hit_test_position ( text_rid , colx ) ;
2023-08-15 08:42:40 +00:00
if ( ! caret_mid_grapheme_enabled ) {
col = TS - > shaped_text_closest_character_pos ( text_rid , col ) ;
}
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
return Point2i ( col , row ) ;
2021-07-09 16:27:09 +00:00
}
2021-11-18 15:55:43 +00:00
Point2i TextEdit : : get_pos_at_line_column ( int p_line , int p_column ) const {
Rect2i rect = get_rect_at_line_column ( p_line , p_column ) ;
2022-04-22 16:52:31 +00:00
return rect . position . x = = - 1 ? rect . position : rect . position + Vector2i ( 0 , get_line_height ( ) ) ;
2021-11-18 15:55:43 +00:00
}
Rect2i TextEdit : : get_rect_at_line_column ( int p_line , int p_column ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Rect2i ( - 1 , - 1 , 0 , 0 ) ) ;
ERR_FAIL_COND_V ( p_column < 0 , Rect2i ( - 1 , - 1 , 0 , 0 ) ) ;
ERR_FAIL_COND_V ( p_column > text [ p_line ] . length ( ) , Rect2i ( - 1 , - 1 , 0 , 0 ) ) ;
2023-09-11 10:32:36 +00:00
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 ) {
// The TextEdit is empty.
return Rect2i ( ) ;
}
2021-11-18 15:55:43 +00:00
if ( line_drawing_cache . size ( ) = = 0 | | ! line_drawing_cache . has ( p_line ) ) {
// Line is not in the cache, which means it's outside of the viewing area.
return Rect2i ( - 1 , - 1 , 0 , 0 ) ;
}
LineDrawingCache cache_entry = line_drawing_cache [ p_line ] ;
int wrap_index = get_line_wrap_index_at_column ( p_line , p_column ) ;
if ( wrap_index > = cache_entry . first_visible_chars . size ( ) ) {
// Line seems to be wrapped beyond the viewable area.
return Rect2i ( - 1 , - 1 , 0 , 0 ) ;
}
int first_visible_char = cache_entry . first_visible_chars [ wrap_index ] ;
int last_visible_char = cache_entry . last_visible_chars [ wrap_index ] ;
if ( p_column < first_visible_char | | p_column > last_visible_char ) {
// Character is outside of the viewing area, no point calculating its position.
return Rect2i ( - 1 , - 1 , 0 , 0 ) ;
}
Point2i pos , size ;
pos . y = cache_entry . y_offset + get_line_height ( ) * wrap_index ;
2023-04-03 16:01:10 +00:00
pos . x = get_total_gutter_width ( ) + theme_cache . style_normal - > get_margin ( SIDE_LEFT ) - get_h_scroll ( ) ;
2021-11-18 15:55:43 +00:00
RID text_rid = text . get_line_data ( p_line ) - > get_line_rid ( wrap_index ) ;
Vector2 col_bounds = TS - > shaped_text_get_grapheme_bounds ( text_rid , p_column ) ;
pos . x + = col_bounds . x ;
size . x = col_bounds . y - col_bounds . x ;
size . y = get_line_height ( ) ;
return Rect2i ( pos , size ) ;
}
2021-07-10 10:41:38 +00:00
int TextEdit : : get_minimap_line_at_pos ( const Point2i & p_pos ) const {
float rows = p_pos . y ;
2023-04-03 16:01:10 +00:00
rows - = theme_cache . style_normal - > get_margin ( SIDE_TOP ) ;
2021-07-10 10:41:38 +00:00
rows / = ( minimap_char_size . y + minimap_line_spacing ) ;
rows + = _get_v_scroll_offset ( ) ;
2021-07-09 16:27:09 +00:00
2023-03-08 22:00:27 +00:00
// Calculate visible lines.
2021-07-10 10:41:38 +00:00
int minimap_visible_lines = get_minimap_visible_lines ( ) ;
int visible_rows = get_visible_line_count ( ) + 1 ;
2022-09-29 09:53:28 +00:00
int first_vis_line = get_first_visible_line ( ) - 1 ;
2021-07-10 10:41:38 +00:00
int draw_amount = visible_rows + ( smooth_scroll_enabled ? 1 : 0 ) ;
2022-09-29 09:53:28 +00:00
draw_amount + = get_line_wrap_count ( first_vis_line + 1 ) ;
2021-07-10 10:41:38 +00:00
int minimap_line_height = ( minimap_char_size . y + minimap_line_spacing ) ;
2021-07-09 16:27:09 +00:00
2023-03-08 22:00:27 +00:00
// Calculate viewport size and y offset.
2021-07-10 10:41:38 +00:00
int viewport_height = ( draw_amount - 1 ) * minimap_line_height ;
int control_height = _get_control_height ( ) - viewport_height ;
2022-09-29 09:53:28 +00:00
int viewport_offset_y = round ( get_scroll_pos_for_line ( first_vis_line + 1 ) * control_height ) / ( ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? ( minimap_visible_lines - draw_amount ) : ( v_scroll - > get_max ( ) - draw_amount ) ) ;
2021-07-10 10:41:38 +00:00
2023-03-08 22:00:27 +00:00
// Calculate the first line.
2021-07-10 10:41:38 +00:00
int num_lines_before = round ( ( viewport_offset_y ) / minimap_line_height ) ;
2022-09-29 09:53:28 +00:00
int minimap_line = ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? - 1 : first_vis_line ;
if ( first_vis_line > 0 & & minimap_line > = 0 ) {
minimap_line - = get_next_visible_line_index_offset_from ( first_vis_line , 0 , - num_lines_before ) . x ;
2021-07-10 10:41:38 +00:00
minimap_line - = ( minimap_line > 0 & & smooth_scroll_enabled ? 1 : 0 ) ;
2022-12-31 21:45:18 +00:00
}
if ( minimap_line < 0 ) {
2021-07-10 10:41:38 +00:00
minimap_line = 0 ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
int row = minimap_line + Math : : floor ( rows ) ;
if ( get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE | | _is_hiding_enabled ( ) ) {
2022-04-29 15:23:32 +00:00
int f_ofs = get_next_visible_line_index_offset_from ( minimap_line , first_visible_line_wrap_ofs , rows + ( 1 * SIGN ( rows ) ) ) . x - 1 ;
2021-07-10 10:41:38 +00:00
if ( rows < 0 ) {
row = minimap_line - f_ofs ;
} else {
row = minimap_line + f_ofs ;
2021-07-09 16:27:09 +00:00
}
}
2024-05-30 16:06:02 +00:00
row = CLAMP ( row , 0 , text . size ( ) - 1 ) ;
2021-07-09 16:27:09 +00:00
2021-07-10 10:41:38 +00:00
return row ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_dragging_cursor ( ) const {
return dragging_selection | | dragging_minimap ;
2021-07-09 16:27:09 +00:00
}
2022-06-03 16:47:37 +00:00
bool TextEdit : : is_mouse_over_selection ( bool p_edges , int p_caret ) const {
2024-01-22 23:26:16 +00:00
Point2i pos = get_line_column_at_pos ( get_local_mouse_pos ( ) ) ;
int line = pos . y ;
int column = pos . x ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
if ( ( p_caret = = - 1 & & get_selection_at_line_column ( line , column , p_edges ) ! = - 1 ) | | ( p_caret ! = - 1 & & _selection_contains ( p_caret , line , column , p_edges ) ) ) {
return true ;
2021-11-24 21:22:40 +00:00
}
2022-06-03 16:47:37 +00:00
return false ;
2021-11-24 21:22:40 +00:00
}
2021-07-10 10:41:38 +00:00
/* Caret */
void TextEdit : : set_caret_type ( CaretType p_type ) {
2022-03-16 07:50:48 +00:00
if ( caret_type = = p_type ) {
return ;
}
2021-07-10 10:41:38 +00:00
caret_type = p_type ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-09 16:27:09 +00:00
}
2021-07-10 10:41:38 +00:00
TextEdit : : CaretType TextEdit : : get_caret_type ( ) const {
return caret_type ;
2021-07-09 16:27:09 +00:00
}
2020-07-25 00:15:23 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_caret_blink_enabled ( const bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( caret_blink_enabled = = p_enabled ) {
return ;
}
2021-07-10 10:41:38 +00:00
caret_blink_enabled = p_enabled ;
2020-07-25 00:15:23 +00:00
2021-07-10 10:41:38 +00:00
if ( has_focus ( ) ) {
if ( p_enabled ) {
caret_blink_timer - > start ( ) ;
} else {
caret_blink_timer - > stop ( ) ;
}
}
draw_caret = true ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_caret_blink_enabled ( ) const {
return caret_blink_enabled ;
2020-07-25 00:15:23 +00:00
}
2022-08-13 12:52:35 +00:00
float TextEdit : : get_caret_blink_interval ( ) const {
2021-07-10 10:41:38 +00:00
return caret_blink_timer - > get_wait_time ( ) ;
2020-07-25 00:15:23 +00:00
}
2022-08-13 12:52:35 +00:00
void TextEdit : : set_caret_blink_interval ( const float p_interval ) {
ERR_FAIL_COND ( p_interval < = 0 ) ;
caret_blink_timer - > set_wait_time ( p_interval ) ;
2021-03-13 14:09:49 +00:00
}
2023-02-07 23:02:48 +00:00
void TextEdit : : set_draw_caret_when_editable_disabled ( bool p_enable ) {
if ( draw_caret_when_editable_disabled = = p_enable ) {
return ;
}
draw_caret_when_editable_disabled = p_enable ;
queue_redraw ( ) ;
}
bool TextEdit : : is_drawing_caret_when_editable_disabled ( ) const {
return draw_caret_when_editable_disabled ;
}
2021-10-08 21:26:13 +00:00
void TextEdit : : set_move_caret_on_right_click_enabled ( const bool p_enabled ) {
move_caret_on_right_click = p_enabled ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_move_caret_on_right_click_enabled ( ) const {
return move_caret_on_right_click ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_caret_mid_grapheme_enabled ( const bool p_enabled ) {
caret_mid_grapheme_enabled = p_enabled ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_caret_mid_grapheme_enabled ( ) const {
return caret_mid_grapheme_enabled ;
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : set_multiple_carets_enabled ( bool p_enabled ) {
multi_carets_enabled = p_enabled ;
if ( ! multi_carets_enabled ) {
remove_secondary_carets ( ) ;
2024-05-02 13:16:32 +00:00
multicaret_edit_count = 0 ;
multicaret_edit_ignore_carets . clear ( ) ;
multicaret_edit_merge_queued = false ;
2022-06-03 16:47:37 +00:00
}
}
bool TextEdit : : is_multiple_carets_enabled ( ) const {
return multi_carets_enabled ;
}
2024-01-22 23:26:16 +00:00
int TextEdit : : add_caret ( int p_line , int p_column ) {
2022-06-03 16:47:37 +00:00
if ( ! multi_carets_enabled ) {
return - 1 ;
}
2024-01-22 23:26:16 +00:00
_cancel_drag_and_drop_text ( ) ;
2022-06-03 16:47:37 +00:00
p_line = CLAMP ( p_line , 0 , text . size ( ) - 1 ) ;
2024-01-22 23:26:16 +00:00
p_column = CLAMP ( p_column , 0 , get_line ( p_line ) . length ( ) ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
if ( ! is_in_mulitcaret_edit ( ) ) {
// Carets cannot overlap.
if ( get_selection_at_line_column ( p_line , p_column , true , false ) ! = - 1 ) {
2022-06-03 16:47:37 +00:00
return - 1 ;
}
}
carets . push_back ( Caret ( ) ) ;
2024-01-22 23:27:19 +00:00
int new_index = carets . size ( ) - 1 ;
set_caret_line ( p_line , false , false , - 1 , new_index ) ;
set_caret_column ( p_column , false , new_index ) ;
_caret_changed ( new_index ) ;
if ( is_in_mulitcaret_edit ( ) ) {
multicaret_edit_ignore_carets . insert ( new_index ) ;
merge_overlapping_carets ( ) ;
}
return new_index ;
2022-06-03 16:47:37 +00:00
}
void TextEdit : : remove_caret ( int p_caret ) {
2022-10-08 12:39:24 +00:00
ERR_FAIL_COND_MSG ( carets . size ( ) < = 1 , " The main caret should not be removed. " ) ;
2022-06-03 16:47:37 +00:00
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
2024-01-22 23:26:16 +00:00
_caret_changed ( p_caret ) ;
2022-06-03 16:47:37 +00:00
carets . remove_at ( p_caret ) ;
2024-01-22 23:26:16 +00:00
if ( drag_caret_index > = 0 ) {
if ( p_caret = = drag_caret_index ) {
drag_caret_index = - 1 ;
} else if ( p_caret < drag_caret_index ) {
drag_caret_index - = 1 ;
}
}
2022-06-03 16:47:37 +00:00
}
2024-07-04 23:29:15 +00:00
void TextEdit : : remove_drag_caret ( ) {
if ( drag_caret_index > = 0 ) {
if ( drag_caret_index < carets . size ( ) ) {
remove_caret ( drag_caret_index ) ;
}
drag_caret_index = - 1 ;
}
}
2022-06-03 16:47:37 +00:00
void TextEdit : : remove_secondary_carets ( ) {
2024-01-22 23:26:16 +00:00
if ( carets . size ( ) = = 1 ) {
return ;
}
_caret_changed ( ) ;
2022-06-03 16:47:37 +00:00
carets . resize ( 1 ) ;
2024-01-22 23:26:16 +00:00
if ( drag_caret_index > = 0 ) {
drag_caret_index = - 1 ;
}
}
int TextEdit : : get_caret_count ( ) const {
// Don't include drag caret.
if ( drag_caret_index > = 0 ) {
return carets . size ( ) - 1 ;
}
return carets . size ( ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
void TextEdit : : add_caret_at_carets ( bool p_below ) {
2024-05-02 13:16:32 +00:00
if ( ! multi_carets_enabled ) {
return ;
}
2024-01-22 23:27:19 +00:00
const int last_line_max_wrap = get_line_wrap_count ( text . size ( ) - 1 ) ;
begin_multicaret_edit ( ) ;
int view_target_caret = - 1 ;
int view_line = p_below ? - 1 : INT_MAX ;
int num_carets = get_caret_count ( ) ;
for ( int i = 0 ; i < num_carets ; i + + ) {
const int caret_line = get_caret_line ( i ) ;
const int caret_column = get_caret_column ( i ) ;
const bool is_selected = has_selection ( i ) | | carets [ i ] . last_fit_x ! = carets [ i ] . selection . origin_last_fit_x ;
const int selection_origin_line = get_selection_origin_line ( i ) ;
const int selection_origin_column = get_selection_origin_column ( i ) ;
const int caret_wrap_index = get_caret_wrap_index ( i ) ;
const int selection_origin_wrap_index = ! is_selected ? - 1 : get_line_wrap_index_at_column ( selection_origin_line , selection_origin_column ) ;
if ( caret_line = = 0 & & ! p_below & & ( caret_wrap_index = = 0 | | selection_origin_wrap_index = = 0 ) ) {
// Can't add above the first line.
continue ;
}
if ( caret_line = = text . size ( ) - 1 & & p_below & & ( caret_wrap_index = = last_line_max_wrap | | selection_origin_wrap_index = = last_line_max_wrap ) ) {
// Can't add below the last line.
continue ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
// Add a new caret.
int new_caret_index = add_caret ( caret_line , caret_column ) ;
2024-05-02 13:16:32 +00:00
ERR_FAIL_COND_MSG ( new_caret_index < 0 , " Failed to add a caret. " ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
// Copy the selection origin and last fit.
set_selection_origin_line ( selection_origin_line , true , - 1 , new_caret_index ) ;
set_selection_origin_column ( selection_origin_column , new_caret_index ) ;
carets . write [ new_caret_index ] . last_fit_x = carets [ i ] . last_fit_x ;
carets . write [ new_caret_index ] . selection . origin_last_fit_x = carets [ i ] . selection . origin_last_fit_x ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
// Move the caret up or down one visible line.
if ( ! p_below ) {
// Move caret up.
if ( caret_wrap_index > 0 ) {
set_caret_line ( caret_line , false , false , caret_wrap_index - 1 , new_caret_index ) ;
} else {
int new_line = caret_line - get_next_visible_line_offset_from ( caret_line - 1 , - 1 ) ;
if ( is_line_wrapped ( new_line ) ) {
set_caret_line ( new_line , false , false , get_line_wrap_count ( new_line ) , new_caret_index ) ;
} else {
set_caret_line ( new_line , false , false , 0 , new_caret_index ) ;
}
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
// Move selection origin up.
if ( is_selected ) {
if ( selection_origin_wrap_index > 0 ) {
set_selection_origin_line ( caret_line , false , selection_origin_wrap_index - 1 , new_caret_index ) ;
} else {
int new_line = selection_origin_line - get_next_visible_line_offset_from ( selection_origin_line - 1 , - 1 ) ;
if ( is_line_wrapped ( new_line ) ) {
set_selection_origin_line ( new_line , false , get_line_wrap_count ( new_line ) , new_caret_index ) ;
} else {
set_selection_origin_line ( new_line , false , 0 , new_caret_index ) ;
}
2022-06-03 16:47:37 +00:00
}
}
2024-01-22 23:27:19 +00:00
if ( get_caret_line ( new_caret_index ) < view_line ) {
view_line = get_caret_line ( new_caret_index ) ;
view_target_caret = new_caret_index ;
}
} else {
// Move caret down.
if ( caret_wrap_index < get_line_wrap_count ( caret_line ) ) {
set_caret_line ( caret_line , false , false , caret_wrap_index + 1 , new_caret_index ) ;
2022-06-03 16:47:37 +00:00
} else {
2024-01-22 23:27:19 +00:00
int new_line = caret_line + get_next_visible_line_offset_from ( CLAMP ( caret_line + 1 , 0 , text . size ( ) - 1 ) , 1 ) ;
set_caret_line ( new_line , false , false , 0 , new_caret_index ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
// Move selection origin down.
if ( is_selected ) {
if ( selection_origin_wrap_index < get_line_wrap_count ( selection_origin_line ) ) {
set_selection_origin_line ( selection_origin_line , false , selection_origin_wrap_index + 1 , new_caret_index ) ;
} else {
int new_line = selection_origin_line + get_next_visible_line_offset_from ( CLAMP ( selection_origin_line + 1 , 0 , text . size ( ) - 1 ) , 1 ) ;
set_selection_origin_line ( new_line , false , 0 , new_caret_index ) ;
}
}
if ( get_caret_line ( new_caret_index ) > view_line ) {
view_line = get_caret_line ( new_caret_index ) ;
view_target_caret = new_caret_index ;
}
}
if ( is_selected ) {
// Make sure selection is active.
select ( get_selection_origin_line ( new_caret_index ) , get_selection_origin_column ( new_caret_index ) , get_caret_line ( new_caret_index ) , get_caret_column ( new_caret_index ) , new_caret_index ) ;
carets . write [ new_caret_index ] . last_fit_x = carets [ i ] . last_fit_x ;
carets . write [ new_caret_index ] . selection . origin_last_fit_x = carets [ i ] . selection . origin_last_fit_x ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
bool check_edges = ! has_selection ( 0 ) | | ! has_selection ( new_caret_index ) ;
bool will_merge_with_main_caret = _selection_contains ( 0 , get_caret_line ( new_caret_index ) , get_caret_column ( new_caret_index ) , check_edges , false ) | | _selection_contains ( new_caret_index , get_caret_line ( 0 ) , get_caret_column ( 0 ) , check_edges , false ) ;
if ( will_merge_with_main_caret ) {
// Move next to the main caret so it stays the main caret after merging.
Caret new_caret = carets [ new_caret_index ] ;
carets . remove_at ( new_caret_index ) ;
carets . insert ( 0 , new_caret ) ;
i + + ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
// Show the topmost caret if added above or bottommost caret if added below.
if ( view_target_caret > = 0 & & view_target_caret < get_caret_count ( ) ) {
adjust_viewport_to_caret ( view_target_caret ) ;
}
merge_overlapping_carets ( ) ;
end_multicaret_edit ( ) ;
}
struct _CaretSortComparator {
_FORCE_INLINE_ bool operator ( ) ( const Vector3i & a , const Vector3i & b ) const {
// x is column, y is line, z is caret index.
if ( a . y = = b . y ) {
return a . x < b . x ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
return a . y < b . y ;
}
} ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
Vector < int > TextEdit : : get_sorted_carets ( bool p_include_ignored_carets ) const {
// Returns caret indexes sorted by selection start or caret position from top to bottom of text.
Vector < Vector3i > caret_line_col_indexes ;
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( ! p_include_ignored_carets & & multicaret_edit_ignore_caret ( i ) ) {
2022-06-03 16:47:37 +00:00
continue ;
}
2024-01-22 23:27:19 +00:00
caret_line_col_indexes . push_back ( Vector3i ( get_selection_from_column ( i ) , get_selection_from_line ( i ) , i ) ) ;
}
caret_line_col_indexes . sort_custom < _CaretSortComparator > ( ) ;
Vector < int > sorted ;
sorted . resize ( caret_line_col_indexes . size ( ) ) ;
for ( int i = 0 ; i < caret_line_col_indexes . size ( ) ; i + + ) {
sorted . set ( i , caret_line_col_indexes [ i ] . z ) ;
}
return sorted ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
void TextEdit : : collapse_carets ( int p_from_line , int p_from_column , int p_to_line , int p_to_column , bool p_inclusive ) {
// Collapse carets in the selected range to the from position.
// Clamp the collapse target position.
int collapse_line = CLAMP ( p_from_line , 0 , text . size ( ) - 1 ) ;
int collapse_column = CLAMP ( p_from_column , 0 , text [ collapse_line ] . length ( ) ) ;
// Swap the lines if they are in the wrong order.
if ( p_from_line > p_to_line ) {
SWAP ( p_from_line , p_to_line ) ;
SWAP ( p_from_column , p_to_column ) ;
}
if ( p_from_line = = p_to_line & & p_from_column > p_to_column ) {
SWAP ( p_from_column , p_to_column ) ;
}
bool any_collapsed = false ;
// Intentionally includes carets in the multicaret_edit_ignore list so that they are moved together.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
bool is_caret_in = _is_line_col_in_range ( get_caret_line ( i ) , get_caret_column ( i ) , p_from_line , p_from_column , p_to_line , p_to_column , p_inclusive ) ;
if ( ! has_selection ( i ) ) {
if ( is_caret_in ) {
// Caret was in the collapsed area.
set_caret_line ( collapse_line , false , true , - 1 , i ) ;
set_caret_column ( collapse_column , false , i ) ;
2024-05-02 13:16:32 +00:00
if ( is_in_mulitcaret_edit ( ) & & get_caret_count ( ) > 1 ) {
2024-01-22 23:27:19 +00:00
multicaret_edit_ignore_carets . insert ( i ) ;
}
any_collapsed = true ;
}
} else {
bool is_origin_in = _is_line_col_in_range ( get_selection_origin_line ( i ) , get_selection_origin_column ( i ) , p_from_line , p_from_column , p_to_line , p_to_column , p_inclusive ) ;
if ( is_caret_in & & is_origin_in ) {
// Selection was completely encapsulated.
deselect ( i ) ;
set_caret_line ( collapse_line , false , true , - 1 , i ) ;
set_caret_column ( collapse_column , false , i ) ;
2024-05-02 13:16:32 +00:00
if ( is_in_mulitcaret_edit ( ) & & get_caret_count ( ) > 1 ) {
2024-01-22 23:27:19 +00:00
multicaret_edit_ignore_carets . insert ( i ) ;
}
any_collapsed = true ;
} else if ( is_caret_in ) {
// Only caret was inside.
set_caret_line ( collapse_line , false , true , - 1 , i ) ;
set_caret_column ( collapse_column , false , i ) ;
any_collapsed = true ;
} else if ( is_origin_in ) {
// Only selection origin was inside.
set_selection_origin_line ( collapse_line , true , - 1 , i ) ;
set_selection_origin_column ( collapse_column , i ) ;
any_collapsed = true ;
2022-06-03 16:47:37 +00:00
}
}
2024-01-22 23:27:19 +00:00
if ( ! p_inclusive & & ! any_collapsed ) {
if ( ( get_caret_line ( i ) = = collapse_line & & get_caret_column ( i ) = = collapse_column ) | | ( get_selection_origin_line ( i ) = = collapse_line & & get_selection_origin_column ( i ) = = collapse_column ) ) {
// Make sure to queue a merge, even if we didn't include it.
any_collapsed = true ;
}
}
}
if ( any_collapsed ) {
merge_overlapping_carets ( ) ;
2022-06-03 16:47:37 +00:00
}
}
2024-01-22 23:27:19 +00:00
void TextEdit : : merge_overlapping_carets ( ) {
if ( is_in_mulitcaret_edit ( ) ) {
// Queue merge to be performed the end of the multicaret edit.
multicaret_edit_merge_queued = true ;
return ;
}
multicaret_edit_merge_queued = false ;
multicaret_edit_ignore_carets . clear ( ) ;
if ( get_caret_count ( ) = = 1 ) {
return ;
}
Vector < int > sorted_carets = get_sorted_carets ( true ) ;
for ( int i = 0 ; i < sorted_carets . size ( ) - 1 ; i + + ) {
int first_caret = sorted_carets [ i ] ;
int second_caret = sorted_carets [ i + 1 ] ;
bool merge_carets ;
if ( ! has_selection ( first_caret ) | | ! has_selection ( second_caret ) ) {
// Merge if touching.
merge_carets = get_selection_from_line ( second_caret ) < get_selection_to_line ( first_caret ) | | ( get_selection_from_line ( second_caret ) = = get_selection_to_line ( first_caret ) & & get_selection_from_column ( second_caret ) < = get_selection_to_column ( first_caret ) ) ;
} else {
// Merge two selections if overlapping.
merge_carets = get_selection_from_line ( second_caret ) < get_selection_to_line ( first_caret ) | | ( get_selection_from_line ( second_caret ) = = get_selection_to_line ( first_caret ) & & get_selection_from_column ( second_caret ) < get_selection_to_column ( first_caret ) ) ;
}
if ( ! merge_carets ) {
continue ;
}
// Save the newest one for Click + Drag.
int caret_to_save = first_caret ;
int caret_to_remove = second_caret ;
if ( first_caret < second_caret ) {
caret_to_save = second_caret ;
caret_to_remove = first_caret ;
}
if ( get_selection_from_line ( caret_to_save ) ! = get_selection_from_line ( caret_to_remove ) | | get_selection_to_line ( caret_to_save ) ! = get_selection_to_line ( caret_to_remove ) | | get_selection_from_column ( caret_to_save ) ! = get_selection_from_column ( caret_to_remove ) | | get_selection_to_column ( caret_to_save ) ! = get_selection_to_column ( caret_to_remove ) ) {
// Selections are not the same, merge them into one bigger selection.
int new_from_line = MIN ( get_selection_from_line ( caret_to_remove ) , get_selection_from_line ( caret_to_save ) ) ;
int new_to_line = MAX ( get_selection_to_line ( caret_to_remove ) , get_selection_to_line ( caret_to_save ) ) ;
int new_from_col ;
int new_to_col ;
if ( get_selection_from_line ( caret_to_remove ) < get_selection_from_line ( caret_to_save ) ) {
new_from_col = get_selection_from_column ( caret_to_remove ) ;
} else if ( get_selection_from_line ( caret_to_remove ) > get_selection_from_line ( caret_to_save ) ) {
new_from_col = get_selection_from_column ( caret_to_save ) ;
2022-10-09 15:07:42 +00:00
} else {
2024-01-22 23:27:19 +00:00
new_from_col = MIN ( get_selection_from_column ( caret_to_remove ) , get_selection_from_column ( caret_to_save ) ) ;
}
if ( get_selection_to_line ( caret_to_remove ) < get_selection_to_line ( caret_to_save ) ) {
new_to_col = get_selection_to_column ( caret_to_save ) ;
} else if ( get_selection_to_line ( caret_to_remove ) > get_selection_to_line ( caret_to_save ) ) {
new_to_col = get_selection_to_column ( caret_to_remove ) ;
} else {
new_to_col = MAX ( get_selection_to_column ( caret_to_remove ) , get_selection_to_column ( caret_to_save ) ) ;
2022-10-09 15:07:42 +00:00
}
2024-01-22 23:27:19 +00:00
// Use the direction from the last caret or the saved one.
int caret_dir_to_copy ;
if ( has_selection ( caret_to_remove ) & & has_selection ( caret_to_save ) ) {
caret_dir_to_copy = caret_to_remove = = get_caret_count ( ) - 1 ? caret_to_remove : caret_to_save ;
} else {
caret_dir_to_copy = ! has_selection ( caret_to_remove ) ? caret_to_save : caret_to_remove ;
}
2022-10-09 15:07:42 +00:00
2024-01-22 23:27:19 +00:00
if ( is_caret_after_selection_origin ( caret_dir_to_copy ) ) {
select ( new_from_line , new_from_col , new_to_line , new_to_col , caret_to_save ) ;
2022-10-09 15:07:42 +00:00
} else {
2024-01-22 23:27:19 +00:00
select ( new_to_line , new_to_col , new_from_line , new_from_col , caret_to_save ) ;
2022-10-09 15:07:42 +00:00
}
}
2024-01-22 23:27:19 +00:00
if ( caret_to_save = = 0 ) {
adjust_viewport_to_caret ( caret_to_save ) ;
2022-10-09 15:07:42 +00:00
}
2024-01-22 23:27:19 +00:00
remove_caret ( caret_to_remove ) ;
// Update the rest of the sorted list.
for ( int j = i ; j < sorted_carets . size ( ) ; j + + ) {
if ( sorted_carets [ j ] > caret_to_remove ) {
// Shift the index since a caret before it was removed.
sorted_carets . write [ j ] - = 1 ;
}
2022-10-09 15:07:42 +00:00
}
2024-01-22 23:27:19 +00:00
// Remove the caret from the sorted array.
sorted_carets . remove_at ( caret_to_remove = = first_caret ? i : i + 1 ) ;
2022-10-09 15:07:42 +00:00
2024-01-22 23:27:19 +00:00
// Process the caret again, since it and the next caret might also overlap.
i - - ;
2022-10-09 15:07:42 +00:00
}
2024-01-22 23:27:19 +00:00
}
2022-10-09 15:07:42 +00:00
2024-01-22 23:27:19 +00:00
// Starts a multicaret edit operation. Call this before iterating over the carets and call [end_multicaret_edit] afterwards.
void TextEdit : : begin_multicaret_edit ( ) {
2024-05-02 13:16:32 +00:00
if ( ! multi_carets_enabled ) {
return ;
}
2024-01-22 23:27:19 +00:00
multicaret_edit_count + + ;
2022-10-09 15:07:42 +00:00
}
2024-01-22 23:27:19 +00:00
void TextEdit : : end_multicaret_edit ( ) {
2024-05-02 13:16:32 +00:00
if ( ! multi_carets_enabled ) {
return ;
}
2024-01-22 23:27:19 +00:00
if ( multicaret_edit_count > 0 ) {
multicaret_edit_count - - ;
}
if ( multicaret_edit_count ! = 0 ) {
return ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
// This was the last multicaret edit operation.
if ( multicaret_edit_merge_queued ) {
merge_overlapping_carets ( ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
multicaret_edit_ignore_carets . clear ( ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
bool TextEdit : : is_in_mulitcaret_edit ( ) const {
return multicaret_edit_count > 0 ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
bool TextEdit : : multicaret_edit_ignore_caret ( int p_caret ) const {
return multicaret_edit_ignore_carets . has ( p_caret ) ;
2022-06-03 16:47:37 +00:00
}
bool TextEdit : : is_caret_visible ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , 0 ) ;
return carets [ p_caret ] . visible ;
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
Point2 TextEdit : : get_caret_draw_pos ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , Point2 ( 0 , 0 ) ) ;
return carets [ p_caret ] . draw_pos ;
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : set_caret_line ( int p_line , bool p_adjust_viewport , bool p_can_be_hidden , int p_wrap_index , int p_caret ) {
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
2021-07-10 10:41:38 +00:00
if ( setting_caret_line ) {
2021-06-15 14:05:01 +00:00
return ;
}
2021-07-10 10:41:38 +00:00
setting_caret_line = true ;
2024-01-22 23:26:16 +00:00
p_line = CLAMP ( p_line , 0 , text . size ( ) - 1 ) ;
2021-06-15 14:05:01 +00:00
2021-07-10 10:41:38 +00:00
if ( ! p_can_be_hidden ) {
2024-01-22 23:26:16 +00:00
if ( _is_line_hidden ( p_line ) ) {
2021-07-10 10:41:38 +00:00
int move_down = get_next_visible_line_offset_from ( p_line , 1 ) - 1 ;
if ( p_line + move_down < = text . size ( ) - 1 & & ! _is_line_hidden ( p_line + move_down ) ) {
p_line + = move_down ;
} else {
int move_up = get_next_visible_line_offset_from ( p_line , - 1 ) - 1 ;
if ( p_line - move_up > 0 & & ! _is_line_hidden ( p_line - move_up ) ) {
p_line - = move_up ;
} else {
2024-01-22 23:26:16 +00:00
WARN_PRINT ( " Caret set to hidden line " + itos ( p_line ) + " and there are no nonhidden lines. " ) ;
2021-07-10 10:41:38 +00:00
}
}
2021-06-15 14:05:01 +00:00
}
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
bool caret_moved = get_caret_line ( p_caret ) ! = p_line ;
carets . write [ p_caret ] . line = p_line ;
2021-06-15 14:05:01 +00:00
2024-01-22 23:26:16 +00:00
int n_col ;
if ( p_wrap_index > = 0 ) {
// Keep caret in same visual x position it was at previously.
n_col = _get_char_pos_for_line ( carets [ p_caret ] . last_fit_x , p_line , p_wrap_index ) ;
if ( n_col ! = 0 & & get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE & & p_wrap_index < get_line_wrap_count ( p_line ) ) {
// Offset by one to not go past the end of the wrapped line.
if ( n_col > = text . get_line_wrap_ranges ( p_line ) [ p_wrap_index ] . y ) {
n_col - = 1 ;
}
2021-06-15 14:05:01 +00:00
}
2024-01-22 23:26:16 +00:00
} else {
// Clamp the column.
n_col = MIN ( get_caret_column ( p_caret ) , get_line ( p_line ) . length ( ) ) ;
2021-07-10 10:41:38 +00:00
}
2022-06-03 16:47:37 +00:00
caret_moved = ( caret_moved | | get_caret_column ( p_caret ) ! = n_col ) ;
carets . write [ p_caret ] . column = n_col ;
2021-06-15 14:05:01 +00:00
2024-01-22 23:26:16 +00:00
// Unselect if the caret moved to the selection origin.
if ( p_wrap_index > = 0 & & has_selection ( p_caret ) & & get_caret_line ( p_caret ) = = get_selection_origin_line ( p_caret ) & & get_caret_column ( p_caret ) = = get_selection_origin_column ( p_caret ) ) {
deselect ( p_caret ) ;
}
2022-02-28 18:50:12 +00:00
if ( is_inside_tree ( ) & & p_adjust_viewport ) {
2022-06-03 16:47:37 +00:00
adjust_viewport_to_caret ( p_caret ) ;
2021-07-10 10:41:38 +00:00
}
setting_caret_line = false ;
2024-01-22 23:26:16 +00:00
if ( caret_moved ) {
_caret_changed ( p_caret ) ;
2021-06-15 14:05:01 +00:00
}
}
2022-06-03 16:47:37 +00:00
int TextEdit : : get_caret_line ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , 0 ) ;
return carets [ p_caret ] . line ;
2021-07-10 10:41:38 +00:00
}
2020-07-25 00:15:23 +00:00
2024-01-22 23:26:16 +00:00
void TextEdit : : set_caret_column ( int p_column , bool p_adjust_viewport , int p_caret ) {
2022-06-03 16:47:37 +00:00
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
2021-07-10 10:41:38 +00:00
2024-01-22 23:26:16 +00:00
p_column = CLAMP ( p_column , 0 , get_line ( get_caret_line ( p_caret ) ) . length ( ) ) ;
bool caret_moved = get_caret_column ( p_caret ) ! = p_column ;
carets . write [ p_caret ] . column = p_column ;
2021-07-10 10:41:38 +00:00
2022-06-03 16:47:37 +00:00
carets . write [ p_caret ] . last_fit_x = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2021-07-10 10:41:38 +00:00
2024-01-22 23:26:16 +00:00
if ( ! has_selection ( p_caret ) ) {
// Set the selection origin last fit x to be the same, so we can tell if there was a selection.
carets . write [ p_caret ] . selection . origin_last_fit_x = carets [ p_caret ] . last_fit_x ;
}
// Unselect if the caret moved to the selection origin.
if ( has_selection ( p_caret ) & & get_caret_line ( p_caret ) = = get_selection_origin_line ( p_caret ) & & get_caret_column ( p_caret ) = = get_selection_origin_column ( p_caret ) ) {
deselect ( p_caret ) ;
}
2022-02-28 18:50:12 +00:00
if ( is_inside_tree ( ) & & p_adjust_viewport ) {
2022-06-03 16:47:37 +00:00
adjust_viewport_to_caret ( p_caret ) ;
2021-07-10 10:41:38 +00:00
}
2024-01-22 23:26:16 +00:00
if ( caret_moved ) {
_caret_changed ( p_caret ) ;
2021-07-10 10:41:38 +00:00
}
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
int TextEdit : : get_caret_column ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , 0 ) ;
return carets [ p_caret ] . column ;
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
int TextEdit : : get_caret_wrap_index ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , 0 ) ;
return get_line_wrap_index_at_column ( get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2020-07-25 00:15:23 +00:00
}
2022-06-03 16:47:37 +00:00
String TextEdit : : get_word_under_caret ( int p_caret ) const {
2024-01-22 23:26:16 +00:00
ERR_FAIL_COND_V ( p_caret > = carets . size ( ) | | p_caret < - 1 , " " ) ;
2022-06-03 16:47:37 +00:00
StringBuilder selected_text ;
for ( int c = 0 ; c < carets . size ( ) ; c + + ) {
if ( p_caret ! = - 1 & & p_caret ! = c ) {
continue ;
}
PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( get_caret_line ( c ) ) - > get_rid ( ) ) ;
for ( int i = 0 ; i < words . size ( ) ; i = i + 2 ) {
if ( words [ i ] < = get_caret_column ( c ) & & words [ i + 1 ] > get_caret_column ( c ) ) {
selected_text + = text [ get_caret_line ( c ) ] . substr ( words [ i ] , words [ i + 1 ] - words [ i ] ) ;
if ( p_caret = = - 1 & & c ! = carets . size ( ) - 1 ) {
selected_text + = " \n " ;
}
}
2021-07-10 10:41:38 +00:00
}
}
2022-06-03 16:47:37 +00:00
return selected_text . as_string ( ) ;
2020-07-25 00:15:23 +00:00
}
2021-07-10 10:41:38 +00:00
/* Selection. */
void TextEdit : : set_selecting_enabled ( const bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( selecting_enabled = = p_enabled ) {
return ;
}
2021-07-10 10:41:38 +00:00
selecting_enabled = p_enabled ;
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
if ( ! selecting_enabled ) {
deselect ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_selecting_enabled ( ) const {
return selecting_enabled ;
2017-11-11 04:07:41 +00:00
}
2020-05-14 12:29:06 +00:00
2021-10-21 21:02:46 +00:00
void TextEdit : : set_deselect_on_focus_loss_enabled ( const bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( deselect_on_focus_loss_enabled = = p_enabled ) {
return ;
}
2021-10-21 21:02:46 +00:00
deselect_on_focus_loss_enabled = p_enabled ;
2022-04-29 15:23:32 +00:00
if ( p_enabled & & has_selection ( ) & & ! has_focus ( ) ) {
2021-10-21 21:02:46 +00:00
deselect ( ) ;
}
}
bool TextEdit : : is_deselect_on_focus_loss_enabled ( ) const {
return deselect_on_focus_loss_enabled ;
}
2022-06-19 14:55:13 +00:00
void TextEdit : : set_drag_and_drop_selection_enabled ( const bool p_enabled ) {
drag_and_drop_selection_enabled = p_enabled ;
}
bool TextEdit : : is_drag_and_drop_selection_enabled ( ) const {
return drag_and_drop_selection_enabled ;
}
2024-01-22 23:26:16 +00:00
void TextEdit : : set_selection_mode ( SelectionMode p_mode ) {
2022-04-29 15:23:32 +00:00
selecting_mode = p_mode ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2021-07-10 10:41:38 +00:00
TextEdit : : SelectionMode TextEdit : : get_selection_mode ( ) const {
2022-04-29 15:23:32 +00:00
return selecting_mode ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : select_all ( ) {
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2021-07-10 10:41:38 +00:00
if ( ! selecting_enabled ) {
return ;
}
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 ) {
return ;
}
2022-04-29 15:23:32 +00:00
2022-06-03 16:47:37 +00:00
remove_secondary_carets ( ) ;
2024-01-22 23:26:16 +00:00
set_selection_mode ( SelectionMode : : SELECTION_MODE_SHIFT ) ;
2022-04-29 15:23:32 +00:00
select ( 0 , 0 , text . size ( ) - 1 , text [ text . size ( ) - 1 ] . length ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : select_word_under_caret ( int p_caret ) {
2024-01-22 23:26:16 +00:00
ERR_FAIL_COND ( p_caret > = carets . size ( ) | | p_caret < - 1 ) ;
2022-06-03 16:47:37 +00:00
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2021-07-10 10:41:38 +00:00
if ( ! selecting_enabled ) {
return ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 ) {
return ;
}
2014-02-10 01:10:30 +00:00
2022-06-03 16:47:37 +00:00
for ( int c = 0 ; c < carets . size ( ) ; c + + ) {
if ( p_caret ! = - 1 & & p_caret ! = c ) {
continue ;
}
2021-07-10 10:41:38 +00:00
2022-06-03 16:47:37 +00:00
if ( has_selection ( c ) ) {
// Allow toggling selection by pressing the shortcut a second time.
// This is also usable as a general-purpose "deselect" shortcut after
// selecting anything.
deselect ( c ) ;
continue ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2022-06-03 16:47:37 +00:00
int begin = 0 ;
int end = 0 ;
const PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( get_caret_line ( c ) ) - > get_rid ( ) ) ;
for ( int i = 0 ; i < words . size ( ) ; i = i + 2 ) {
if ( ( words [ i ] < = get_caret_column ( c ) & & words [ i + 1 ] > = get_caret_column ( c ) ) | | ( i = = words . size ( ) - 2 & & get_caret_column ( c ) = = words [ i + 1 ] ) ) {
begin = words [ i ] ;
end = words [ i + 1 ] ;
break ;
}
}
// No word found.
if ( begin = = 0 & & end = = 0 ) {
continue ;
}
2022-04-22 16:52:31 +00:00
2022-10-11 19:32:06 +00:00
select ( get_caret_line ( c ) , begin , get_caret_line ( c ) , end , c ) ;
2022-06-03 16:47:37 +00:00
}
2022-10-11 19:32:06 +00:00
merge_overlapping_carets ( ) ;
2016-05-28 16:25:45 +00:00
}
2022-10-19 17:35:14 +00:00
void TextEdit : : add_selection_for_next_occurrence ( ) {
if ( ! selecting_enabled | | ! is_multiple_carets_enabled ( ) ) {
return ;
}
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 ) {
return ;
}
2023-11-24 21:52:01 +00:00
_push_current_op ( ) ;
2022-10-19 17:35:14 +00:00
// Always use the last caret, to correctly search for
// the next occurrence that comes after this caret.
int caret = get_caret_count ( ) - 1 ;
if ( ! has_selection ( caret ) ) {
select_word_under_caret ( caret ) ;
return ;
}
const String & highlighted_text = get_selected_text ( caret ) ;
int column = get_selection_from_column ( caret ) + 1 ;
2024-05-01 01:48:22 +00:00
int line = get_selection_from_line ( caret ) ;
2022-10-19 17:35:14 +00:00
const Point2i next_occurrence = search ( highlighted_text , SEARCH_MATCH_CASE , line , column ) ;
if ( next_occurrence . x = = - 1 | | next_occurrence . y = = - 1 ) {
return ;
}
int to_column = get_selection_to_column ( caret ) + 1 ;
int end = next_occurrence . x + ( to_column - column ) ;
int new_caret = add_caret ( next_occurrence . y , end ) ;
if ( new_caret ! = - 1 ) {
select ( next_occurrence . y , next_occurrence . x , next_occurrence . y , end , new_caret ) ;
2024-05-01 01:48:22 +00:00
_unhide_carets ( ) ;
2022-10-19 17:35:14 +00:00
adjust_viewport_to_caret ( new_caret ) ;
merge_overlapping_carets ( ) ;
}
}
2024-02-02 22:10:55 +00:00
void TextEdit : : skip_selection_for_next_occurrence ( ) {
if ( ! selecting_enabled ) {
return ;
}
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 ) {
return ;
}
// Always use the last caret, to correctly search for
// the next occurrence that comes after this caret.
int caret = get_caret_count ( ) - 1 ;
// Supports getting the text under caret without selecting it.
// It allows to use this shortcut to simply jump to the next (under caret) word.
// Due to const and &(reference) presence, ternary operator is a way to avoid errors and warnings.
const String & searched_text = has_selection ( caret ) ? get_selected_text ( caret ) : get_word_under_caret ( caret ) ;
2024-05-01 01:48:22 +00:00
int column = get_selection_from_column ( caret ) + 1 ;
int line = get_selection_from_line ( caret ) ;
2024-02-02 22:10:55 +00:00
const Point2i next_occurrence = search ( searched_text , SEARCH_MATCH_CASE , line , column ) ;
if ( next_occurrence . x = = - 1 | | next_occurrence . y = = - 1 ) {
return ;
}
2024-05-01 01:48:22 +00:00
int to_column = get_selection_to_column ( caret ) + 1 ;
2024-02-02 22:10:55 +00:00
int end = next_occurrence . x + ( to_column - column ) ;
int new_caret = add_caret ( next_occurrence . y , end ) ;
if ( new_caret ! = - 1 ) {
select ( next_occurrence . y , next_occurrence . x , next_occurrence . y , end , new_caret ) ;
2024-05-01 01:48:22 +00:00
_unhide_carets ( ) ;
2024-02-02 22:10:55 +00:00
adjust_viewport_to_caret ( new_caret ) ;
merge_overlapping_carets ( ) ;
}
// Deselect word under previous caret.
if ( has_selection ( caret ) ) {
select_word_under_caret ( caret ) ;
}
// Remove previous caret.
if ( get_caret_count ( ) > 1 ) {
remove_caret ( caret ) ;
}
}
2024-01-22 23:26:16 +00:00
void TextEdit : : select ( int p_origin_line , int p_origin_column , int p_caret_line , int p_caret_column , int p_caret ) {
ERR_FAIL_INDEX ( p_caret , get_caret_count ( ) ) ;
p_caret_line = CLAMP ( p_caret_line , 0 , text . size ( ) - 1 ) ;
p_caret_column = CLAMP ( p_caret_column , 0 , text [ p_caret_line ] . length ( ) ) ;
set_caret_line ( p_caret_line , false , true , - 1 , p_caret ) ;
set_caret_column ( p_caret_column , false , p_caret ) ;
2021-07-10 10:41:38 +00:00
if ( ! selecting_enabled ) {
return ;
}
2024-01-22 23:26:16 +00:00
p_origin_line = CLAMP ( p_origin_line , 0 , text . size ( ) - 1 ) ;
p_origin_column = CLAMP ( p_origin_column , 0 , text [ p_origin_line ] . length ( ) ) ;
set_selection_origin_line ( p_origin_line , true , - 1 , p_caret ) ;
set_selection_origin_column ( p_origin_column , p_caret ) ;
2021-07-10 10:41:38 +00:00
2024-01-22 23:26:16 +00:00
bool had_selection = has_selection ( p_caret ) ;
bool activate = p_origin_line ! = p_caret_line | | p_origin_column ! = p_caret_column ;
carets . write [ p_caret ] . selection . active = activate ;
if ( had_selection ! = activate ) {
_selection_changed ( p_caret ) ;
}
}
2021-07-10 10:41:38 +00:00
2024-01-22 23:26:16 +00:00
bool TextEdit : : has_selection ( int p_caret ) const {
ERR_FAIL_COND_V ( p_caret > = carets . size ( ) | | p_caret < - 1 , false ) ;
if ( p_caret > = 0 ) {
return carets [ p_caret ] . selection . active ;
}
for ( int i = 0 ; i < carets . size ( ) ; i + + ) {
if ( carets [ i ] . selection . active ) {
return true ;
}
}
return false ;
}
2021-07-10 10:41:38 +00:00
2024-01-22 23:26:16 +00:00
String TextEdit : : get_selected_text ( int p_caret ) {
ERR_FAIL_COND_V ( p_caret > = carets . size ( ) | | p_caret < - 1 , " " ) ;
2021-07-10 10:41:38 +00:00
2024-01-22 23:26:16 +00:00
if ( p_caret > = 0 ) {
if ( ! has_selection ( p_caret ) ) {
return " " ;
2021-07-10 10:41:38 +00:00
}
2024-01-22 23:26:16 +00:00
return _base_get_text ( get_selection_from_line ( p_caret ) , get_selection_from_column ( p_caret ) , get_selection_to_line ( p_caret ) , get_selection_to_column ( p_caret ) ) ;
2021-07-10 10:41:38 +00:00
}
2016-05-28 16:25:45 +00:00
2024-01-22 23:26:16 +00:00
StringBuilder selected_text ;
Vector < int > sorted_carets = get_sorted_carets ( ) ;
for ( int i = 0 ; i < sorted_carets . size ( ) ; i + + ) {
int caret_index = sorted_carets [ i ] ;
if ( ! has_selection ( caret_index ) ) {
continue ;
}
if ( selected_text . get_string_length ( ) ! = 0 ) {
selected_text + = " \n " ;
}
selected_text + = _base_get_text ( get_selection_from_line ( caret_index ) , get_selection_from_column ( caret_index ) , get_selection_to_line ( caret_index ) , get_selection_to_column ( caret_index ) ) ;
}
return selected_text . as_string ( ) ;
2016-05-28 16:25:45 +00:00
}
2024-01-22 23:27:19 +00:00
int TextEdit : : get_selection_at_line_column ( int p_line , int p_column , bool p_include_edges , bool p_only_selections ) const {
2024-01-22 23:26:16 +00:00
// Return the caret index of the found selection, or -1.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2024-01-22 23:27:19 +00:00
if ( _selection_contains ( i , p_line , p_column , p_include_edges , p_only_selections ) ) {
2024-01-22 23:26:16 +00:00
return i ;
}
}
return - 1 ;
}
Vector < Point2i > TextEdit : : get_line_ranges_from_carets ( bool p_only_selections , bool p_merge_adjacent ) const {
// Get a series of line ranges that cover all lines that have a caret or selection.
// For each Point2i range, x is the first line and y is the last line.
Vector < Point2i > ret ;
int last_to_line = INT_MIN ;
Vector < int > sorted_carets = get_sorted_carets ( ) ;
for ( int i = 0 ; i < sorted_carets . size ( ) ; i + + ) {
int caret_index = sorted_carets [ i ] ;
if ( p_only_selections & & ! has_selection ( caret_index ) ) {
2022-06-03 16:47:37 +00:00
continue ;
}
2024-01-22 23:26:16 +00:00
Point2i range = Point2i ( get_selection_from_line ( caret_index ) , get_selection_to_line ( caret_index ) ) ;
if ( has_selection ( caret_index ) & & get_selection_to_column ( caret_index ) = = 0 ) {
// Dont include selection end line if it ends at column 0.
range . y - - ;
}
if ( range . x = = last_to_line | | ( p_merge_adjacent & & range . x - 1 = = last_to_line ) ) {
// Merge if starts on the same line or adjacent line.
ret . write [ ret . size ( ) - 1 ] . y = range . y ;
} else {
ret . append ( range ) ;
}
last_to_line = range . y ;
}
return ret ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:26:16 +00:00
TypedArray < Vector2i > TextEdit : : get_line_ranges_from_carets_typed_array ( bool p_only_selections , bool p_merge_adjacent ) const {
// Wrapper for `get_line_ranges_from_carets` to return a datatype that can be exposed.
TypedArray < Vector2i > ret ;
Vector < Point2i > ranges = get_line_ranges_from_carets ( p_only_selections , p_merge_adjacent ) ;
for ( const Point2i & range : ranges ) {
ret . push_back ( range ) ;
}
return ret ;
}
void TextEdit : : set_selection_origin_line ( int p_line , bool p_can_be_hidden , int p_wrap_index , int p_caret ) {
if ( ! selecting_enabled ) {
return ;
}
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
p_line = CLAMP ( p_line , 0 , text . size ( ) - 1 ) ;
if ( ! p_can_be_hidden ) {
if ( _is_line_hidden ( p_line ) ) {
int move_down = get_next_visible_line_offset_from ( p_line , 1 ) - 1 ;
if ( p_line + move_down < = text . size ( ) - 1 & & ! _is_line_hidden ( p_line + move_down ) ) {
p_line + = move_down ;
} else {
int move_up = get_next_visible_line_offset_from ( p_line , - 1 ) - 1 ;
if ( p_line - move_up > 0 & & ! _is_line_hidden ( p_line - move_up ) ) {
p_line - = move_up ;
} else {
WARN_PRINT ( " Selection origin set to hidden line " + itos ( p_line ) + " and there are no nonhidden lines. " ) ;
}
}
2022-06-03 16:47:37 +00:00
}
}
2024-01-22 23:26:16 +00:00
bool selection_moved = get_selection_origin_line ( p_caret ) ! = p_line ;
carets . write [ p_caret ] . selection . origin_line = p_line ;
int n_col ;
if ( p_wrap_index > = 0 ) {
// Keep selection origin in same visual x position it was at previously.
n_col = _get_char_pos_for_line ( carets [ p_caret ] . selection . origin_last_fit_x , p_line , p_wrap_index ) ;
if ( n_col ! = 0 & & get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE & & p_wrap_index < get_line_wrap_count ( p_line ) ) {
// Offset by one to not go past the end of the wrapped line.
if ( n_col > = text . get_line_wrap_ranges ( p_line ) [ p_wrap_index ] . y ) {
n_col - = 1 ;
}
}
} else {
// Clamp the column.
n_col = MIN ( get_selection_origin_column ( p_caret ) , get_line ( p_line ) . length ( ) ) ;
}
selection_moved = ( selection_moved | | get_selection_origin_column ( p_caret ) ! = n_col ) ;
carets . write [ p_caret ] . selection . origin_column = n_col ;
// Unselect if the selection origin moved to the caret.
if ( p_wrap_index > = 0 & & has_selection ( p_caret ) & & get_caret_line ( p_caret ) = = get_selection_origin_line ( p_caret ) & & get_caret_column ( p_caret ) = = get_selection_origin_column ( p_caret ) ) {
deselect ( p_caret ) ;
}
if ( selection_moved & & has_selection ( p_caret ) ) {
_selection_changed ( p_caret ) ;
}
}
void TextEdit : : set_selection_origin_column ( int p_column , int p_caret ) {
if ( ! selecting_enabled ) {
return ;
}
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
p_column = CLAMP ( p_column , 0 , get_line ( get_selection_origin_line ( p_caret ) ) . length ( ) ) ;
bool selection_moved = get_selection_origin_column ( p_caret ) ! = p_column ;
carets . write [ p_caret ] . selection . origin_column = p_column ;
carets . write [ p_caret ] . selection . origin_last_fit_x = _get_column_x_offset_for_line ( get_selection_origin_column ( p_caret ) , get_selection_origin_line ( p_caret ) , get_selection_origin_column ( p_caret ) ) ;
// Unselect if the selection origin moved to the caret.
if ( has_selection ( p_caret ) & & get_caret_line ( p_caret ) = = get_selection_origin_line ( p_caret ) & & get_caret_column ( p_caret ) = = get_selection_origin_column ( p_caret ) ) {
deselect ( p_caret ) ;
}
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
if ( selection_moved & & has_selection ( p_caret ) ) {
_selection_changed ( p_caret ) ;
2021-07-10 10:41:38 +00:00
}
2016-07-11 14:20:01 +00:00
}
2024-01-22 23:26:16 +00:00
int TextEdit : : get_selection_origin_line ( int p_caret ) const {
2022-06-03 16:47:37 +00:00
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , - 1 ) ;
2024-01-22 23:26:16 +00:00
return carets [ p_caret ] . selection . origin_line ;
2021-07-10 10:41:38 +00:00
}
2016-03-16 21:20:42 +00:00
2024-01-22 23:26:16 +00:00
int TextEdit : : get_selection_origin_column ( int p_caret ) const {
2022-06-03 16:47:37 +00:00
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , - 1 ) ;
2024-01-22 23:26:16 +00:00
return carets [ p_caret ] . selection . origin_column ;
2021-07-10 10:41:38 +00:00
}
2016-03-16 21:20:42 +00:00
2022-06-03 16:47:37 +00:00
int TextEdit : : get_selection_from_line ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , - 1 ) ;
2024-01-22 23:26:16 +00:00
if ( ! has_selection ( p_caret ) ) {
return carets [ p_caret ] . line ;
}
return MIN ( carets [ p_caret ] . selection . origin_line , carets [ p_caret ] . line ) ;
2021-07-10 10:41:38 +00:00
}
2016-03-17 20:35:04 +00:00
2022-06-03 16:47:37 +00:00
int TextEdit : : get_selection_from_column ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , - 1 ) ;
2024-01-22 23:26:16 +00:00
if ( ! has_selection ( p_caret ) ) {
return carets [ p_caret ] . column ;
}
if ( carets [ p_caret ] . selection . origin_line < carets [ p_caret ] . line ) {
return carets [ p_caret ] . selection . origin_column ;
} else if ( carets [ p_caret ] . selection . origin_line > carets [ p_caret ] . line ) {
return carets [ p_caret ] . column ;
} else {
return MIN ( carets [ p_caret ] . selection . origin_column , carets [ p_caret ] . column ) ;
}
2021-07-10 10:41:38 +00:00
}
2016-03-17 20:35:04 +00:00
2022-06-03 16:47:37 +00:00
int TextEdit : : get_selection_to_line ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , - 1 ) ;
2024-01-22 23:26:16 +00:00
if ( ! has_selection ( p_caret ) ) {
return carets [ p_caret ] . line ;
}
return MAX ( carets [ p_caret ] . selection . origin_line , carets [ p_caret ] . line ) ;
2021-07-10 10:41:38 +00:00
}
2016-03-17 20:35:04 +00:00
2022-06-03 16:47:37 +00:00
int TextEdit : : get_selection_to_column ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , - 1 ) ;
2024-01-22 23:26:16 +00:00
if ( ! has_selection ( p_caret ) ) {
return carets [ p_caret ] . column ;
}
if ( carets [ p_caret ] . selection . origin_line < carets [ p_caret ] . line ) {
return carets [ p_caret ] . column ;
} else if ( carets [ p_caret ] . selection . origin_line > carets [ p_caret ] . line ) {
return carets [ p_caret ] . selection . origin_column ;
} else {
return MAX ( carets [ p_caret ] . selection . origin_column , carets [ p_caret ] . column ) ;
}
}
bool TextEdit : : is_caret_after_selection_origin ( int p_caret ) const {
ERR_FAIL_INDEX_V ( p_caret , carets . size ( ) , false ) ;
if ( ! has_selection ( p_caret ) ) {
return true ;
}
return carets [ p_caret ] . line > carets [ p_caret ] . selection . origin_line | | ( carets [ p_caret ] . line = = carets [ p_caret ] . selection . origin_line & & carets [ p_caret ] . column > = carets [ p_caret ] . selection . origin_column ) ;
2016-03-16 21:20:42 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : deselect ( int p_caret ) {
2024-01-22 23:26:16 +00:00
ERR_FAIL_COND ( p_caret > = carets . size ( ) | | p_caret < - 1 ) ;
bool selection_changed = false ;
if ( p_caret > = 0 ) {
selection_changed = carets . write [ p_caret ] . selection . active ;
carets . write [ p_caret ] . selection . active = false ;
} else {
for ( int i = 0 ; i < carets . size ( ) ; i + + ) {
selection_changed | = carets . write [ i ] . selection . active ;
carets . write [ i ] . selection . active = false ;
2022-06-03 16:47:37 +00:00
}
}
2024-01-22 23:26:16 +00:00
if ( selection_changed ) {
_selection_changed ( p_caret ) ;
}
2014-02-10 01:10:30 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : delete_selection ( int p_caret ) {
2024-01-22 23:26:16 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2022-06-03 16:47:37 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
2022-06-03 16:47:37 +00:00
if ( p_caret ! = - 1 & & p_caret ! = i ) {
continue ;
}
2024-01-22 23:27:19 +00:00
if ( p_caret = = - 1 & & multicaret_edit_ignore_caret ( i ) ) {
continue ;
}
2022-06-03 16:47:37 +00:00
if ( ! has_selection ( i ) ) {
continue ;
}
2016-03-08 23:00:52 +00:00
2024-01-22 23:27:19 +00:00
int selection_from_line = get_selection_from_line ( i ) ;
int selection_from_column = get_selection_from_column ( i ) ;
int selection_to_line = get_selection_to_line ( i ) ;
int selection_to_column = get_selection_to_column ( i ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
_remove_text ( selection_from_line , selection_from_column , selection_to_line , selection_to_column ) ;
_offset_carets_after ( selection_to_line , selection_to_column , selection_from_line , selection_from_column ) ;
merge_overlapping_carets ( ) ;
deselect ( i ) ;
set_caret_line ( selection_from_line , false , false , - 1 , i ) ;
set_caret_column ( selection_from_column , i = = 0 , i ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2022-06-03 16:47:37 +00:00
end_complex_operation ( ) ;
2021-07-10 10:41:38 +00:00
}
2016-03-08 23:00:52 +00:00
2021-11-18 15:55:43 +00:00
/* Line wrapping. */
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line_wrapping_mode ( LineWrappingMode p_wrapping_mode ) {
if ( line_wrapping_mode ! = p_wrapping_mode ) {
line_wrapping_mode = p_wrapping_mode ;
_update_wrap_at_column ( true ) ;
2023-03-12 11:21:00 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
TextEdit : : LineWrappingMode TextEdit : : get_line_wrapping_mode ( ) const {
return line_wrapping_mode ;
}
2016-03-08 23:00:52 +00:00
2023-03-12 11:21:00 +00:00
void TextEdit : : set_autowrap_mode ( TextServer : : AutowrapMode p_mode ) {
if ( autowrap_mode = = p_mode ) {
return ;
}
autowrap_mode = p_mode ;
if ( get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE ) {
_update_wrap_at_column ( true ) ;
queue_redraw ( ) ;
}
}
TextServer : : AutowrapMode TextEdit : : get_autowrap_mode ( ) const {
return autowrap_mode ;
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_line_wrapped ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
if ( get_line_wrapping_mode ( ) = = LineWrappingMode : : LINE_WRAPPING_NONE ) {
return false ;
}
return text . get_line_wrap_amount ( p_line ) > 0 ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_line_wrap_count ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2018-01-08 21:50:33 +00:00
2021-07-10 10:41:38 +00:00
if ( ! is_line_wrapped ( p_line ) ) {
return 0 ;
}
2018-01-08 21:50:33 +00:00
2021-07-10 10:41:38 +00:00
return text . get_line_wrap_amount ( p_line ) ;
}
2018-08-27 17:01:12 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_line_wrap_index_at_column ( int p_line , int p_column ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
ERR_FAIL_COND_V ( p_column < 0 , 0 ) ;
ERR_FAIL_COND_V ( p_column > text [ p_line ] . length ( ) , 0 ) ;
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
if ( ! is_line_wrapped ( p_line ) ) {
return 0 ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
/* Loop through wraps in the line text until we get to the column. */
int wrap_index = 0 ;
int col = 0 ;
Vector < String > lines = get_line_wrapped_text ( p_line ) ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
wrap_index = i ;
2023-12-13 16:06:14 +00:00
col + = lines [ wrap_index ] . length ( ) ;
2021-07-10 10:41:38 +00:00
if ( col > p_column ) {
2015-01-02 18:08:40 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2021-07-10 10:41:38 +00:00
}
return wrap_index ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
Vector < String > TextEdit : : get_line_wrapped_text ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Vector < String > ( ) ) ;
Vector < String > lines ;
if ( ! is_line_wrapped ( p_line ) ) {
lines . push_back ( text [ p_line ] ) ;
return lines ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
const String & line_text = text [ p_line ] ;
Vector < Vector2i > line_ranges = text . get_line_wrap_ranges ( p_line ) ;
for ( int i = 0 ; i < line_ranges . size ( ) ; i + + ) {
lines . push_back ( line_text . substr ( line_ranges [ i ] . x , line_ranges [ i ] . y - line_ranges [ i ] . x ) ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
return lines ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
/* Viewport */
// Scrolling.
2021-10-08 21:26:13 +00:00
void TextEdit : : set_smooth_scroll_enabled ( const bool p_enabled ) {
v_scroll - > set_smooth_scroll_enabled ( p_enabled ) ;
smooth_scroll_enabled = p_enabled ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_smooth_scroll_enabled ( ) const {
return smooth_scroll_enabled ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_scroll_past_end_of_file_enabled ( const bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( scroll_past_end_of_file_enabled = = p_enabled ) {
return ;
}
2021-07-10 10:41:38 +00:00
scroll_past_end_of_file_enabled = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2017-11-12 23:12:17 +00:00
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_scroll_past_end_of_file_enabled ( ) const {
return scroll_past_end_of_file_enabled ;
}
2018-01-26 01:41:17 +00:00
2022-09-28 04:55:06 +00:00
VScrollBar * TextEdit : : get_v_scroll_bar ( ) const {
return v_scroll ;
}
HScrollBar * TextEdit : : get_h_scroll_bar ( ) const {
return h_scroll ;
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_v_scroll ( double p_scroll ) {
v_scroll - > set_value ( p_scroll ) ;
int max_v_scroll = v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ;
if ( p_scroll > = max_v_scroll - 1.0 ) {
_scroll_moved ( v_scroll - > get_value ( ) ) ;
2017-11-12 23:12:17 +00:00
}
}
2021-07-10 10:41:38 +00:00
double TextEdit : : get_v_scroll ( ) const {
return v_scroll - > get_value ( ) ;
}
2017-12-26 06:53:16 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_h_scroll ( int p_scroll ) {
if ( p_scroll < 0 ) {
p_scroll = 0 ;
2020-05-14 14:41:43 +00:00
}
2021-07-10 10:41:38 +00:00
h_scroll - > set_value ( p_scroll ) ;
}
2018-01-26 01:41:17 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_h_scroll ( ) const {
return h_scroll - > get_value ( ) ;
}
void TextEdit : : set_v_scroll_speed ( float p_speed ) {
2023-03-08 22:00:27 +00:00
// Prevent setting a vertical scroll speed value under 1.
2022-02-12 13:40:49 +00:00
ERR_FAIL_COND ( p_speed < 1.0 ) ;
2021-07-10 10:41:38 +00:00
v_scroll_speed = p_speed ;
}
float TextEdit : : get_v_scroll_speed ( ) const {
return v_scroll_speed ;
2018-01-26 01:41:17 +00:00
}
2022-06-20 23:19:54 +00:00
void TextEdit : : set_fit_content_height_enabled ( const bool p_enabled ) {
if ( fit_content_height = = p_enabled ) {
return ;
}
fit_content_height = p_enabled ;
update_minimum_size ( ) ;
}
bool TextEdit : : is_fit_content_height_enabled ( ) const {
return fit_content_height ;
}
2021-07-10 10:41:38 +00:00
double TextEdit : : get_scroll_pos_for_line ( int p_line , int p_wrap_index ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
ERR_FAIL_COND_V ( p_wrap_index < 0 , 0 ) ;
ERR_FAIL_COND_V ( p_wrap_index > get_line_wrap_count ( p_line ) , 0 ) ;
if ( get_line_wrapping_mode ( ) = = LineWrappingMode : : LINE_WRAPPING_NONE & & ! _is_hiding_enabled ( ) ) {
return p_line ;
2020-05-14 14:41:43 +00:00
}
2017-12-26 06:53:16 +00:00
2021-12-11 02:03:24 +00:00
double new_line_scroll_pos = 0.0 ;
if ( p_line > 0 ) {
new_line_scroll_pos = get_visible_line_count_in_range ( 0 , MIN ( p_line - 1 , text . size ( ) - 1 ) ) ;
}
2021-07-10 10:41:38 +00:00
new_line_scroll_pos + = p_wrap_index ;
return new_line_scroll_pos ;
2017-12-26 06:53:16 +00:00
}
2021-07-10 10:41:38 +00:00
// Visible lines.
void TextEdit : : set_line_as_first_visible ( int p_line , int p_wrap_index ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_COND ( p_wrap_index < 0 ) ;
ERR_FAIL_COND ( p_wrap_index > get_line_wrap_count ( p_line ) ) ;
set_v_scroll ( get_scroll_pos_for_line ( p_line , p_wrap_index ) ) ;
}
2017-11-12 23:12:17 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_first_visible_line ( ) const {
2022-04-29 15:23:32 +00:00
return CLAMP ( first_visible_line , 0 , text . size ( ) - 1 ) ;
2017-11-12 23:12:17 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line_as_center_visible ( int p_line , int p_wrap_index ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_COND ( p_wrap_index < 0 ) ;
ERR_FAIL_COND ( p_wrap_index > get_line_wrap_count ( p_line ) ) ;
2018-01-12 05:25:04 +00:00
2021-07-10 10:41:38 +00:00
int visible_rows = get_visible_line_count ( ) ;
2022-04-22 16:52:31 +00:00
Point2i next_line = get_next_visible_line_index_offset_from ( p_line , p_wrap_index , ( - visible_rows / 2 ) - 1 ) ;
2021-07-10 10:41:38 +00:00
int first_line = p_line - next_line . x + 1 ;
2018-01-12 05:25:04 +00:00
2022-04-22 16:52:31 +00:00
if ( first_line < 0 ) {
set_v_scroll ( 0 ) ;
return ;
}
2021-07-10 10:41:38 +00:00
set_v_scroll ( get_scroll_pos_for_line ( first_line , next_line . y ) ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line_as_last_visible ( int p_line , int p_wrap_index ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_COND ( p_wrap_index < 0 ) ;
ERR_FAIL_COND ( p_wrap_index > get_line_wrap_count ( p_line ) ) ;
Point2i next_line = get_next_visible_line_index_offset_from ( p_line , p_wrap_index , - get_visible_line_count ( ) - 1 ) ;
int first_line = p_line - next_line . x + 1 ;
2022-04-22 16:52:31 +00:00
// Adding _get_visible_lines_offset is not 100% correct as we end up showing almost p_line + 1, however, it provides a
// better user experience. Therefore we need to special case < visible line count, else showing line 0 is impossible.
if ( get_visible_line_count_in_range ( 0 , p_line ) < get_visible_line_count ( ) + 1 ) {
set_v_scroll ( 0 ) ;
return ;
}
2023-09-30 09:00:24 +00:00
set_v_scroll ( Math : : round ( get_scroll_pos_for_line ( first_line , next_line . y ) + _get_visible_lines_offset ( ) ) ) ;
2021-03-13 14:09:49 +00:00
}
2021-07-10 10:41:38 +00:00
int TextEdit : : get_last_full_visible_line ( ) const {
int first_vis_line = get_first_visible_line ( ) ;
int last_vis_line = 0 ;
2022-04-29 15:23:32 +00:00
last_vis_line = first_vis_line + get_next_visible_line_index_offset_from ( first_vis_line , first_visible_line_wrap_ofs , get_visible_line_count ( ) ) . x - 1 ;
2021-07-10 10:41:38 +00:00
last_vis_line = CLAMP ( last_vis_line , 0 , text . size ( ) - 1 ) ;
return last_vis_line ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_last_full_visible_line_wrap_index ( ) const {
int first_vis_line = get_first_visible_line ( ) ;
2022-04-29 15:23:32 +00:00
return get_next_visible_line_index_offset_from ( first_vis_line , first_visible_line_wrap_ofs , get_visible_line_count ( ) ) . y ;
2021-07-10 10:41:38 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_visible_line_count ( ) const {
return _get_control_height ( ) / get_line_height ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-10-31 18:34:54 +00:00
int TextEdit : : get_visible_line_count_in_range ( int p_from_line , int p_to_line ) const {
ERR_FAIL_INDEX_V ( p_from_line , text . size ( ) , 0 ) ;
ERR_FAIL_INDEX_V ( p_to_line , text . size ( ) , 0 ) ;
2022-03-11 01:25:59 +00:00
2023-03-08 22:00:27 +00:00
// So we can handle inputs in whatever order.
2022-03-11 01:25:59 +00:00
if ( p_from_line > p_to_line ) {
SWAP ( p_from_line , p_to_line ) ;
}
2021-10-31 18:34:54 +00:00
2023-03-08 22:00:27 +00:00
// Returns the total number of (lines + wrapped - hidden).
2021-07-10 10:41:38 +00:00
if ( ! _is_hiding_enabled ( ) & & get_line_wrapping_mode ( ) = = LineWrappingMode : : LINE_WRAPPING_NONE ) {
2021-12-11 02:03:24 +00:00
return ( p_to_line - p_from_line ) + 1 ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
int total_rows = 0 ;
2021-10-31 18:34:54 +00:00
for ( int i = p_from_line ; i < = p_to_line ; i + + ) {
2021-07-10 10:41:38 +00:00
if ( ! text . is_hidden ( i ) ) {
total_rows + + ;
total_rows + = get_line_wrap_count ( i ) ;
}
2015-01-02 18:08:40 +00:00
}
2021-07-10 10:41:38 +00:00
return total_rows ;
2014-02-10 01:10:30 +00:00
}
2021-10-31 18:34:54 +00:00
int TextEdit : : get_total_visible_line_count ( ) const {
return get_visible_line_count_in_range ( 0 , text . size ( ) - 1 ) ;
}
2023-03-08 22:00:27 +00:00
// Auto adjust.
2022-06-03 16:47:37 +00:00
void TextEdit : : adjust_viewport_to_caret ( int p_caret ) {
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
2021-07-10 10:41:38 +00:00
// Make sure Caret is visible on the screen.
scrolling = false ;
minimap_clicked = false ;
2020-12-07 11:32:00 +00:00
2022-06-03 16:47:37 +00:00
int cur_line = get_caret_line ( p_caret ) ;
int cur_wrap = get_caret_wrap_index ( p_caret ) ;
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
int first_vis_line = get_first_visible_line ( ) ;
2022-04-29 15:23:32 +00:00
int first_vis_wrap = first_visible_line_wrap_ofs ;
2021-07-10 10:41:38 +00:00
int last_vis_line = get_last_full_visible_line ( ) ;
int last_vis_wrap = get_last_full_visible_line_wrap_index ( ) ;
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
if ( cur_line < first_vis_line | | ( cur_line = = first_vis_line & & cur_wrap < first_vis_wrap ) ) {
// Caret is above screen.
set_line_as_first_visible ( cur_line , cur_wrap ) ;
} else if ( cur_line > last_vis_line | | ( cur_line = = last_vis_line & & cur_wrap > last_vis_wrap ) ) {
// Caret is below screen.
set_line_as_last_visible ( cur_line , cur_wrap ) ;
}
2016-03-08 23:00:52 +00:00
2023-04-03 16:01:10 +00:00
int visible_width = get_size ( ) . width - theme_cache . style_normal - > get_minimum_size ( ) . width - gutters_width - gutter_padding ;
2021-07-10 10:41:38 +00:00
if ( draw_minimap ) {
visible_width - = minimap_width ;
}
if ( v_scroll - > is_visible_in_tree ( ) ) {
visible_width - = v_scroll - > get_combined_minimum_size ( ) . width ;
2020-05-14 14:41:43 +00:00
}
2021-07-10 10:41:38 +00:00
visible_width - = 20 ; // Give it a little more space.
2016-03-08 23:00:52 +00:00
2024-07-16 15:19:58 +00:00
if ( visible_width < = 0 ) {
// Not resized yet.
return ;
}
2023-03-12 11:21:00 +00:00
Vector2i caret_pos ;
2018-01-13 10:38:01 +00:00
2023-03-12 11:21:00 +00:00
// Get position of the start of caret.
2024-01-22 17:34:12 +00:00
if ( has_ime_text ( ) & & ime_selection . x ! = 0 ) {
2023-03-12 11:21:00 +00:00
caret_pos . x = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) + ime_selection . x , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
} else {
caret_pos . x = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
}
2019-04-21 18:19:31 +00:00
2023-03-12 11:21:00 +00:00
// Get position of the end of caret.
2024-01-22 17:34:12 +00:00
if ( has_ime_text ( ) ) {
2023-03-12 11:21:00 +00:00
if ( ime_selection . y ! = 0 ) {
caret_pos . y = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) + ime_selection . x + ime_selection . y , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2021-07-10 10:41:38 +00:00
} else {
2023-03-12 11:21:00 +00:00
caret_pos . y = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) + ime_text . size ( ) , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2016-06-14 11:48:34 +00:00
}
2023-03-12 11:21:00 +00:00
} else {
caret_pos . y = caret_pos . x ;
}
2016-03-08 23:00:52 +00:00
2023-03-12 11:21:00 +00:00
if ( MAX ( caret_pos . x , caret_pos . y ) > ( first_visible_col + visible_width ) ) {
first_visible_col = MAX ( caret_pos . x , caret_pos . y ) - visible_width + 1 ;
}
2021-07-10 10:41:38 +00:00
2023-03-12 11:21:00 +00:00
if ( MIN ( caret_pos . x , caret_pos . y ) < first_visible_col ) {
first_visible_col = MIN ( caret_pos . x , caret_pos . y ) ;
2016-06-13 19:40:28 +00:00
}
2022-04-29 15:23:32 +00:00
h_scroll - > set_value ( first_visible_col ) ;
2021-07-10 10:41:38 +00:00
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : center_viewport_to_caret ( int p_caret ) {
ERR_FAIL_INDEX ( p_caret , carets . size ( ) ) ;
2021-07-10 10:41:38 +00:00
// Move viewport so the caret is in the center of the screen.
scrolling = false ;
minimap_clicked = false ;
2016-03-08 23:00:52 +00:00
2022-06-03 16:47:37 +00:00
set_line_as_center_visible ( get_caret_line ( p_caret ) , get_caret_wrap_index ( p_caret ) ) ;
2023-04-03 16:01:10 +00:00
int visible_width = get_size ( ) . width - theme_cache . style_normal - > get_minimum_size ( ) . width - gutters_width - gutter_padding ;
2021-07-10 10:41:38 +00:00
if ( draw_minimap ) {
visible_width - = minimap_width ;
}
if ( v_scroll - > is_visible_in_tree ( ) ) {
visible_width - = v_scroll - > get_combined_minimum_size ( ) . width ;
2020-05-14 14:41:43 +00:00
}
2021-07-10 10:41:38 +00:00
visible_width - = 20 ; // Give it a little more space.
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
if ( get_line_wrapping_mode ( ) ! = LineWrappingMode : : LINE_WRAPPING_NONE ) {
// Center x offset.
2018-01-13 10:38:01 +00:00
2021-07-10 10:41:38 +00:00
Vector2i caret_pos ;
// Get position of the start of caret.
2024-01-22 17:34:12 +00:00
if ( has_ime_text ( ) & & ime_selection . x ! = 0 ) {
2022-06-03 16:47:37 +00:00
caret_pos . x = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) + ime_selection . x , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
caret_pos . x = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2021-07-10 10:41:38 +00:00
}
// Get position of the end of caret.
2024-01-22 17:34:12 +00:00
if ( has_ime_text ( ) ) {
2021-07-10 10:41:38 +00:00
if ( ime_selection . y ! = 0 ) {
2022-06-03 16:47:37 +00:00
caret_pos . y = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) + ime_selection . x + ime_selection . y , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2021-07-10 10:41:38 +00:00
} else {
2022-06-03 16:47:37 +00:00
caret_pos . y = _get_column_x_offset_for_line ( get_caret_column ( p_caret ) + ime_text . size ( ) , get_caret_line ( p_caret ) , get_caret_column ( p_caret ) ) ;
2020-05-14 14:41:43 +00:00
}
2021-07-10 10:41:38 +00:00
} else {
caret_pos . y = caret_pos . x ;
}
2022-04-29 15:23:32 +00:00
if ( MAX ( caret_pos . x , caret_pos . y ) > ( first_visible_col + visible_width ) ) {
first_visible_col = MAX ( caret_pos . x , caret_pos . y ) - visible_width + 1 ;
2021-07-10 10:41:38 +00:00
}
2022-04-29 15:23:32 +00:00
if ( MIN ( caret_pos . x , caret_pos . y ) < first_visible_col ) {
first_visible_col = MIN ( caret_pos . x , caret_pos . y ) ;
2016-06-14 11:48:34 +00:00
}
2021-07-10 10:41:38 +00:00
} else {
2022-04-29 15:23:32 +00:00
first_visible_col = 0 ;
2015-01-02 18:08:40 +00:00
}
2022-04-29 15:23:32 +00:00
h_scroll - > set_value ( first_visible_col ) ;
2019-10-13 14:34:28 +00:00
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
/* Minimap */
2021-10-08 21:26:13 +00:00
void TextEdit : : set_draw_minimap ( bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( draw_minimap = = p_enabled ) {
return ;
2021-07-10 10:41:38 +00:00
}
2022-03-16 07:50:48 +00:00
draw_minimap = p_enabled ;
_update_wrap_at_column ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_drawing_minimap ( ) const {
return draw_minimap ;
2014-04-26 12:42:19 +00:00
}
2014-02-10 01:10:30 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_minimap_width ( int p_minimap_width ) {
2022-03-16 07:50:48 +00:00
if ( minimap_width = = p_minimap_width ) {
return ;
2015-01-02 18:08:40 +00:00
}
2022-03-16 07:50:48 +00:00
minimap_width = p_minimap_width ;
_update_wrap_at_column ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_minimap_width ( ) const {
return minimap_width ;
2014-04-26 12:42:19 +00:00
}
2014-02-10 01:10:30 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_minimap_visible_lines ( ) const {
return _get_control_height ( ) / ( minimap_char_size . y + minimap_line_spacing ) ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
/* Gutters. */
void TextEdit : : add_gutter ( int p_at ) {
if ( p_at < 0 | | p_at > gutters . size ( ) ) {
gutters . push_back ( GutterInfo ( ) ) ;
} else {
gutters . insert ( p_at , GutterInfo ( ) ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2022-05-11 22:15:37 +00:00
text . add_gutter ( p_at ) ;
2022-08-26 10:00:14 +00:00
_update_gutter_width ( ) ;
2021-07-10 10:41:38 +00:00
emit_signal ( SNAME ( " gutter_added " ) ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : remove_gutter ( int p_gutter ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2021-07-03 22:17:03 +00:00
gutters . remove_at ( p_gutter ) ;
2021-07-10 10:41:38 +00:00
2022-05-11 22:15:37 +00:00
text . remove_gutter ( p_gutter ) ;
2022-08-26 10:00:14 +00:00
_update_gutter_width ( ) ;
2021-07-10 10:41:38 +00:00
emit_signal ( SNAME ( " gutter_removed " ) ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2016-03-11 18:10:01 +00:00
}
2021-07-10 10:41:38 +00:00
int TextEdit : : get_gutter_count ( ) const {
return gutters . size ( ) ;
2018-05-03 17:03:20 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_gutter_name ( int p_gutter , const String & p_name ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
gutters . write [ p_gutter ] . name = p_name ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
String TextEdit : : get_gutter_name ( int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , " " ) ;
return gutters [ p_gutter ] . name ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_gutter_type ( int p_gutter , GutterType p_type ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( gutters [ p_gutter ] . type = = p_type ) {
return ;
}
2021-07-10 10:41:38 +00:00
gutters . write [ p_gutter ] . type = p_type ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2019-04-23 23:33:20 +00:00
}
2021-07-10 10:41:38 +00:00
TextEdit : : GutterType TextEdit : : get_gutter_type ( int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , GUTTER_TYPE_STRING ) ;
return gutters [ p_gutter ] . type ;
2019-04-23 23:33:20 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_gutter_width ( int p_gutter , int p_width ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2021-08-27 19:05:05 +00:00
if ( gutters [ p_gutter ] . width = = p_width ) {
return ;
}
2021-07-10 10:41:38 +00:00
gutters . write [ p_gutter ] . width = p_width ;
_update_gutter_width ( ) ;
2017-09-06 01:02:51 +00:00
}
2021-07-10 10:41:38 +00:00
int TextEdit : : get_gutter_width ( int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , - 1 ) ;
return gutters [ p_gutter ] . width ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
2021-07-10 10:41:38 +00:00
int TextEdit : : get_total_gutter_width ( ) const {
return gutters_width + gutter_padding ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_gutter_draw ( int p_gutter , bool p_draw ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2021-08-27 19:05:05 +00:00
if ( gutters [ p_gutter ] . draw = = p_draw ) {
return ;
}
2021-07-10 10:41:38 +00:00
gutters . write [ p_gutter ] . draw = p_draw ;
_update_gutter_width ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_gutter_drawn ( int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , false ) ;
return gutters [ p_gutter ] . draw ;
}
2016-09-12 13:52:29 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_gutter_clickable ( int p_gutter , bool p_clickable ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( gutters [ p_gutter ] . clickable = = p_clickable ) {
return ;
}
2021-07-10 10:41:38 +00:00
gutters . write [ p_gutter ] . clickable = p_clickable ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
bool TextEdit : : is_gutter_clickable ( int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , false ) ;
return gutters [ p_gutter ] . clickable ;
}
void TextEdit : : set_gutter_overwritable ( int p_gutter , bool p_overwritable ) {
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
gutters . write [ p_gutter ] . overwritable = p_overwritable ;
}
bool TextEdit : : is_gutter_overwritable ( int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , false ) ;
return gutters [ p_gutter ] . overwritable ;
}
void TextEdit : : merge_gutters ( int p_from_line , int p_to_line ) {
ERR_FAIL_INDEX ( p_from_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_to_line , text . size ( ) ) ;
if ( p_from_line = = p_to_line ) {
return ;
2020-05-14 14:41:43 +00:00
}
2021-07-10 10:41:38 +00:00
for ( int i = 0 ; i < gutters . size ( ) ; i + + ) {
if ( ! gutters [ i ] . overwritable ) {
continue ;
2017-08-02 17:20:27 +00:00
}
2021-07-10 10:41:38 +00:00
if ( text . get_line_gutter_text ( p_from_line , i ) ! = " " ) {
text . set_line_gutter_text ( p_to_line , i , text . get_line_gutter_text ( p_from_line , i ) ) ;
text . set_line_gutter_item_color ( p_to_line , i , text . get_line_gutter_item_color ( p_from_line , i ) ) ;
}
if ( text . get_line_gutter_icon ( p_from_line , i ) . is_valid ( ) ) {
text . set_line_gutter_icon ( p_to_line , i , text . get_line_gutter_icon ( p_from_line , i ) ) ;
text . set_line_gutter_item_color ( p_to_line , i , text . get_line_gutter_item_color ( p_from_line , i ) ) ;
}
if ( text . get_line_gutter_metadata ( p_from_line , i ) ! = " " ) {
text . set_line_gutter_metadata ( p_to_line , i , text . get_line_gutter_metadata ( p_from_line , i ) ) ;
}
if ( text . is_line_gutter_clickable ( p_from_line , i ) ) {
text . set_line_gutter_clickable ( p_to_line , i , true ) ;
}
2016-09-12 13:52:29 +00:00
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2016-09-12 13:52:29 +00:00
2021-11-07 17:26:15 +00:00
void TextEdit : : set_gutter_custom_draw ( int p_gutter , const Callable & p_draw_callback ) {
2021-07-10 10:41:38 +00:00
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( gutters [ p_gutter ] . custom_draw_callback = = p_draw_callback ) {
return ;
}
2021-11-07 17:26:15 +00:00
gutters . write [ p_gutter ] . custom_draw_callback = p_draw_callback ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2016-09-12 13:52:29 +00:00
}
2021-07-10 10:41:38 +00:00
// Line gutters.
void TextEdit : : set_line_gutter_metadata ( int p_line , int p_gutter , const Variant & p_metadata ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
text . set_line_gutter_metadata ( p_line , p_gutter , p_metadata ) ;
}
2015-11-07 12:39:03 +00:00
2021-07-10 10:41:38 +00:00
Variant TextEdit : : get_line_gutter_metadata ( int p_line , int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , " " ) ;
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , " " ) ;
return text . get_line_gutter_metadata ( p_line , p_gutter ) ;
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line_gutter_text ( int p_line , int p_gutter , const String & p_text ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( text . get_line_gutter_text ( p_line , p_gutter ) = = p_text ) {
return ;
}
2021-07-10 10:41:38 +00:00
text . set_line_gutter_text ( p_line , p_gutter , p_text ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2016-03-08 23:00:52 +00:00
2021-07-10 10:41:38 +00:00
String TextEdit : : get_line_gutter_text ( int p_line , int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , " " ) ;
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , " " ) ;
return text . get_line_gutter_text ( p_line , p_gutter ) ;
}
void TextEdit : : set_line_gutter_icon ( int p_line , int p_gutter , const Ref < Texture2D > & p_icon ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( text . get_line_gutter_icon ( p_line , p_gutter ) = = p_icon ) {
return ;
}
2021-07-10 10:41:38 +00:00
text . set_line_gutter_icon ( p_line , p_gutter , p_icon ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
Ref < Texture2D > TextEdit : : get_line_gutter_icon ( int p_line , int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Ref < Texture2D > ( ) ) ;
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , Ref < Texture2D > ( ) ) ;
return text . get_line_gutter_icon ( p_line , p_gutter ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line_gutter_item_color ( int p_line , int p_gutter , const Color & p_color ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( text . get_line_gutter_item_color ( p_line , p_gutter ) = = p_color ) {
return ;
}
2021-07-10 10:41:38 +00:00
text . set_line_gutter_item_color ( p_line , p_gutter , p_color ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-10 10:41:38 +00:00
Color TextEdit : : get_line_gutter_item_color ( int p_line , int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Color ( ) ) ;
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , Color ( ) ) ;
return text . get_line_gutter_item_color ( p_line , p_gutter ) ;
2014-11-12 14:39:21 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_line_gutter_clickable ( int p_line , int p_gutter , bool p_clickable ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_gutter , gutters . size ( ) ) ;
text . set_line_gutter_clickable ( p_line , p_gutter , p_clickable ) ;
2014-11-12 14:39:21 +00:00
}
2014-02-10 01:10:30 +00:00
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_line_gutter_clickable ( int p_line , int p_gutter ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
ERR_FAIL_INDEX_V ( p_gutter , gutters . size ( ) , false ) ;
return text . is_line_gutter_clickable ( p_line , p_gutter ) ;
2017-10-22 12:38:00 +00:00
}
2021-07-10 10:41:38 +00:00
// Line style
void TextEdit : : set_line_background_color ( int p_line , const Color & p_color ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( text . get_line_background_color ( p_line ) = = p_color ) {
return ;
}
2021-07-10 10:41:38 +00:00
text . set_line_background_color ( p_line , p_color ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2017-10-22 12:38:00 +00:00
}
2021-07-10 10:41:38 +00:00
Color TextEdit : : get_line_background_color ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Color ( ) ) ;
return text . get_line_background_color ( p_line ) ;
2015-10-17 13:29:54 +00:00
}
2016-05-16 23:25:17 +00:00
2021-07-10 10:41:38 +00:00
/* Syntax Highlighting. */
void TextEdit : : set_syntax_highlighter ( Ref < SyntaxHighlighter > p_syntax_highlighter ) {
2022-03-16 07:50:48 +00:00
if ( syntax_highlighter = = p_syntax_highlighter & & syntax_highlighter . is_valid ( ) = = p_syntax_highlighter . is_valid ( ) ) {
return ;
}
2021-07-10 10:41:38 +00:00
syntax_highlighter = p_syntax_highlighter ;
if ( syntax_highlighter . is_valid ( ) ) {
syntax_highlighter - > set_text_edit ( this ) ;
2018-11-29 17:21:43 +00:00
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2016-05-16 23:25:17 +00:00
}
2021-07-10 10:41:38 +00:00
Ref < SyntaxHighlighter > TextEdit : : get_syntax_highlighter ( ) const {
return syntax_highlighter ;
2016-09-29 07:12:45 +00:00
}
2016-09-12 13:52:29 +00:00
2021-07-10 10:41:38 +00:00
/* Visual. */
void TextEdit : : set_highlight_current_line ( bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( highlight_current_line = = p_enabled ) {
return ;
}
2021-07-10 10:41:38 +00:00
highlight_current_line = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2017-11-09 20:46:29 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_highlight_current_line_enabled ( ) const {
return highlight_current_line ;
2019-08-08 00:09:46 +00:00
}
2021-07-10 10:41:38 +00:00
void TextEdit : : set_highlight_all_occurrences ( const bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( highlight_all_occurrences = = p_enabled ) {
return ;
}
2021-07-10 10:41:38 +00:00
highlight_all_occurrences = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-08-09 09:06:36 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_highlight_all_occurrences_enabled ( ) const {
return highlight_all_occurrences ;
2019-08-08 00:09:46 +00:00
}
2024-05-29 11:06:43 +00:00
void TextEdit : : set_use_default_word_separators ( bool p_enabled ) {
text . set_use_default_word_separators ( p_enabled ) ;
}
bool TextEdit : : is_default_word_separators_enabled ( ) const {
return text . is_default_word_separators_enabled ( ) ;
}
// Set word separators. Combine default separators with custom separators if those options are enabled.
void TextEdit : : set_custom_word_separators ( const String & p_separators ) {
text . set_custom_word_separators ( p_separators ) ;
}
void TextEdit : : Text : : set_custom_word_separators ( const String & p_separators ) {
if ( custom_word_separators = = p_separators ) {
return ;
}
custom_word_separators = p_separators ;
invalidate_all_lines ( ) ;
}
bool TextEdit : : is_custom_word_separators_enabled ( ) const {
return text . is_custom_word_separators_enabled ( ) ;
}
String TextEdit : : get_custom_word_separators ( ) const {
return text . get_custom_word_separators ( ) ;
}
// Enable or disable custom word separators.
void TextEdit : : set_use_custom_word_separators ( bool p_enabled ) {
text . set_use_custom_word_separators ( p_enabled ) ;
}
String TextEdit : : get_default_word_separators ( ) const {
return text . get_default_word_separators ( ) ;
}
2021-10-08 21:26:13 +00:00
void TextEdit : : set_draw_control_chars ( bool p_enabled ) {
if ( draw_control_chars ! = p_enabled ) {
draw_control_chars = p_enabled ;
2021-08-14 16:28:07 +00:00
if ( menu ) {
menu - > set_item_checked ( menu - > get_item_index ( MENU_DISPLAY_UCC ) , draw_control_chars ) ;
}
2021-07-10 10:41:38 +00:00
text . set_draw_control_chars ( draw_control_chars ) ;
2022-01-20 07:30:42 +00:00
text . invalidate_font ( ) ;
2022-01-23 20:07:35 +00:00
_update_placeholder ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
2020-08-09 09:06:36 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : get_draw_control_chars ( ) const {
return draw_control_chars ;
2021-07-16 21:36:05 +00:00
}
2021-10-08 21:26:13 +00:00
void TextEdit : : set_draw_tabs ( bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( draw_tabs = = p_enabled ) {
return ;
}
2021-10-08 21:26:13 +00:00
draw_tabs = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2016-05-16 23:25:17 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_drawing_tabs ( ) const {
return draw_tabs ;
2020-09-18 07:36:10 +00:00
}
2021-07-10 10:41:38 +00:00
2021-10-08 21:26:13 +00:00
void TextEdit : : set_draw_spaces ( bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( draw_spaces = = p_enabled ) {
return ;
}
2021-10-08 21:26:13 +00:00
draw_spaces = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-09-18 07:36:10 +00:00
}
2021-07-10 10:41:38 +00:00
bool TextEdit : : is_drawing_spaces ( ) const {
return draw_spaces ;
2020-09-18 07:36:10 +00:00
}
2023-09-19 12:36:38 +00:00
Color TextEdit : : get_font_color ( ) const {
return theme_cache . font_color ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _bind_methods ( ) {
2021-07-10 10:41:38 +00:00
/* Text */
// Text properties
ClassDB : : bind_method ( D_METHOD ( " has_ime_text " ) , & TextEdit : : has_ime_text ) ;
2024-01-22 17:34:12 +00:00
ClassDB : : bind_method ( D_METHOD ( " cancel_ime " ) , & TextEdit : : cancel_ime ) ;
ClassDB : : bind_method ( D_METHOD ( " apply_ime " ) , & TextEdit : : apply_ime ) ;
2021-07-10 10:41:38 +00:00
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_editable " , " enabled " ) , & TextEdit : : set_editable ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_editable " ) , & TextEdit : : is_editable ) ;
2016-03-08 23:00:52 +00:00
2020-09-18 07:36:10 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text_direction " , " direction " ) , & TextEdit : : set_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text_direction " ) , & TextEdit : : get_text_direction ) ;
2021-07-10 10:41:38 +00:00
2020-09-18 07:36:10 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_language " , " language " ) , & TextEdit : : set_language ) ;
ClassDB : : bind_method ( D_METHOD ( " get_language " ) , & TextEdit : : get_language ) ;
ClassDB : : bind_method ( D_METHOD ( " set_structured_text_bidi_override " , " parser " ) , & TextEdit : : set_structured_text_bidi_override ) ;
ClassDB : : bind_method ( D_METHOD ( " get_structured_text_bidi_override " ) , & TextEdit : : get_structured_text_bidi_override ) ;
ClassDB : : bind_method ( D_METHOD ( " set_structured_text_bidi_override_options " , " args " ) , & TextEdit : : set_structured_text_bidi_override_options ) ;
ClassDB : : bind_method ( D_METHOD ( " get_structured_text_bidi_override_options " ) , & TextEdit : : get_structured_text_bidi_override_options ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_tab_size " , " size " ) , & TextEdit : : set_tab_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_tab_size " ) , & TextEdit : : get_tab_size ) ;
2024-02-19 11:41:12 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_indent_wrapped_lines " , " enabled " ) , & TextEdit : : set_indent_wrapped_lines ) ;
ClassDB : : bind_method ( D_METHOD ( " is_indent_wrapped_lines " ) , & TextEdit : : is_indent_wrapped_lines ) ;
2021-07-10 10:41:38 +00:00
// User controls
ClassDB : : bind_method ( D_METHOD ( " set_overtype_mode_enabled " , " enabled " ) , & TextEdit : : set_overtype_mode_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_overtype_mode_enabled " ) , & TextEdit : : is_overtype_mode_enabled ) ;
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_context_menu_enabled " , " enabled " ) , & TextEdit : : set_context_menu_enabled ) ;
2017-11-09 20:46:29 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_context_menu_enabled " ) , & TextEdit : : is_context_menu_enabled ) ;
2021-07-10 10:41:38 +00:00
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_shortcut_keys_enabled " , " enabled " ) , & TextEdit : : set_shortcut_keys_enabled ) ;
2019-08-08 00:09:46 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_shortcut_keys_enabled " ) , & TextEdit : : is_shortcut_keys_enabled ) ;
2021-07-10 10:41:38 +00:00
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_virtual_keyboard_enabled " , " enabled " ) , & TextEdit : : set_virtual_keyboard_enabled ) ;
2020-08-09 09:06:36 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_virtual_keyboard_enabled " ) , & TextEdit : : is_virtual_keyboard_enabled ) ;
2017-12-16 19:34:16 +00:00
2021-10-12 07:43:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_middle_mouse_paste_enabled " , " enabled " ) , & TextEdit : : set_middle_mouse_paste_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_middle_mouse_paste_enabled " ) , & TextEdit : : is_middle_mouse_paste_enabled ) ;
2021-07-10 10:41:38 +00:00
// Text manipulation
ClassDB : : bind_method ( D_METHOD ( " clear " ) , & TextEdit : : clear ) ;
2017-03-05 15:44:50 +00:00
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text " , " text " ) , & TextEdit : : set_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text " ) , & TextEdit : : get_text ) ;
2022-05-09 09:47:10 +00:00
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_line_count " ) , & TextEdit : : get_line_count ) ;
2017-03-05 15:44:50 +00:00
2022-01-23 20:07:35 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_placeholder " , " text " ) , & TextEdit : : set_placeholder ) ;
ClassDB : : bind_method ( D_METHOD ( " get_placeholder " ) , & TextEdit : : get_placeholder ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_line " , " line " , " new_text " ) , & TextEdit : : set_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line " , " line " ) , & TextEdit : : get_line ) ;
2017-02-13 11:47:24 +00:00
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_line_width " , " line " , " wrap_index " ) , & TextEdit : : get_line_width , DEFVAL ( - 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_height " ) , & TextEdit : : get_line_height ) ;
2017-02-13 11:47:24 +00:00
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_indent_level " , " line " ) , & TextEdit : : get_indent_level ) ;
ClassDB : : bind_method ( D_METHOD ( " get_first_non_whitespace_column " , " line " ) , & TextEdit : : get_first_non_whitespace_column ) ;
2017-02-13 11:47:24 +00:00
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " swap_lines " , " from_line " , " to_line " ) , & TextEdit : : swap_lines ) ;
2017-09-27 17:24:05 +00:00
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " insert_line_at " , " line " , " text " ) , & TextEdit : : insert_line_at ) ;
2024-01-22 23:27:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_line_at " , " line " , " move_carets_down " ) , & TextEdit : : remove_line_at , DEFVAL ( true ) ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " insert_text_at_caret " , " text " , " caret_index " ) , & TextEdit : : insert_text_at_caret , DEFVAL ( - 1 ) ) ;
2021-07-09 13:05:52 +00:00
2024-01-22 23:27:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " insert_text " , " text " , " line " , " column " , " before_selection_begin " , " before_selection_end " ) , & TextEdit : : insert_text , DEFVAL ( true ) , DEFVAL ( false ) ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_text " , " from_line " , " from_column " , " to_line " , " to_column " ) , & TextEdit : : remove_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_last_unhidden_line " ) , & TextEdit : : get_last_unhidden_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_next_visible_line_offset_from " , " line " , " visible_amount " ) , & TextEdit : : get_next_visible_line_offset_from ) ;
2021-07-10 13:01:25 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_next_visible_line_index_offset_from " , " line " , " wrap_index " , " visible_amount " ) , & TextEdit : : get_next_visible_line_index_offset_from ) ;
2021-07-08 15:48:58 +00:00
// Overridable actions
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " backspace " , " caret_index " ) , & TextEdit : : backspace , DEFVAL ( - 1 ) ) ;
2021-07-08 15:48:58 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " cut " , " caret_index " ) , & TextEdit : : cut , DEFVAL ( - 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " copy " , " caret_index " ) , & TextEdit : : copy , DEFVAL ( - 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " paste " , " caret_index " ) , & TextEdit : : paste , DEFVAL ( - 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " paste_primary_clipboard " , " caret_index " ) , & TextEdit : : paste_primary_clipboard , DEFVAL ( - 1 ) ) ;
2021-07-08 15:48:58 +00:00
2022-06-03 16:47:37 +00:00
GDVIRTUAL_BIND ( _handle_unicode_input , " unicode_char " , " caret_index " )
GDVIRTUAL_BIND ( _backspace , " caret_index " )
GDVIRTUAL_BIND ( _cut , " caret_index " )
GDVIRTUAL_BIND ( _copy , " caret_index " )
GDVIRTUAL_BIND ( _paste , " caret_index " )
GDVIRTUAL_BIND ( _paste_primary_clipboard , " caret_index " )
2021-07-08 15:48:58 +00:00
2021-07-10 10:41:38 +00:00
// Context Menu
BIND_ENUM_CONSTANT ( MENU_CUT ) ;
BIND_ENUM_CONSTANT ( MENU_COPY ) ;
BIND_ENUM_CONSTANT ( MENU_PASTE ) ;
BIND_ENUM_CONSTANT ( MENU_CLEAR ) ;
BIND_ENUM_CONSTANT ( MENU_SELECT_ALL ) ;
BIND_ENUM_CONSTANT ( MENU_UNDO ) ;
BIND_ENUM_CONSTANT ( MENU_REDO ) ;
2023-01-26 17:59:41 +00:00
BIND_ENUM_CONSTANT ( MENU_SUBMENU_TEXT_DIR ) ;
2021-07-10 10:41:38 +00:00
BIND_ENUM_CONSTANT ( MENU_DIR_INHERITED ) ;
BIND_ENUM_CONSTANT ( MENU_DIR_AUTO ) ;
BIND_ENUM_CONSTANT ( MENU_DIR_LTR ) ;
BIND_ENUM_CONSTANT ( MENU_DIR_RTL ) ;
BIND_ENUM_CONSTANT ( MENU_DISPLAY_UCC ) ;
2023-01-26 17:59:41 +00:00
BIND_ENUM_CONSTANT ( MENU_SUBMENU_INSERT_UCC ) ;
2021-07-10 10:41:38 +00:00
BIND_ENUM_CONSTANT ( MENU_INSERT_LRM ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_RLM ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_LRE ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_RLE ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_LRO ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_RLO ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_PDF ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_ALM ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_LRI ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_RLI ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_FSI ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_PDI ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_ZWJ ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_ZWNJ ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_WJ ) ;
BIND_ENUM_CONSTANT ( MENU_INSERT_SHY ) ;
BIND_ENUM_CONSTANT ( MENU_MAX ) ;
/* Versioning */
2022-06-03 16:47:37 +00:00
BIND_ENUM_CONSTANT ( ACTION_NONE ) ;
BIND_ENUM_CONSTANT ( ACTION_TYPING ) ;
BIND_ENUM_CONSTANT ( ACTION_BACKSPACE ) ;
BIND_ENUM_CONSTANT ( ACTION_DELETE ) ;
ClassDB : : bind_method ( D_METHOD ( " start_action " , " action " ) , & TextEdit : : start_action ) ;
ClassDB : : bind_method ( D_METHOD ( " end_action " ) , & TextEdit : : end_complex_operation ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " begin_complex_operation " ) , & TextEdit : : begin_complex_operation ) ;
ClassDB : : bind_method ( D_METHOD ( " end_complex_operation " ) , & TextEdit : : end_complex_operation ) ;
2021-08-17 03:41:46 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_undo " ) , & TextEdit : : has_undo ) ;
ClassDB : : bind_method ( D_METHOD ( " has_redo " ) , & TextEdit : : has_redo ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " undo " ) , & TextEdit : : undo ) ;
ClassDB : : bind_method ( D_METHOD ( " redo " ) , & TextEdit : : redo ) ;
ClassDB : : bind_method ( D_METHOD ( " clear_undo_history " ) , & TextEdit : : clear_undo_history ) ;
ClassDB : : bind_method ( D_METHOD ( " tag_saved_version " ) , & TextEdit : : tag_saved_version ) ;
ClassDB : : bind_method ( D_METHOD ( " get_version " ) , & TextEdit : : get_version ) ;
ClassDB : : bind_method ( D_METHOD ( " get_saved_version " ) , & TextEdit : : get_saved_version ) ;
/* Search */
BIND_ENUM_CONSTANT ( SEARCH_MATCH_CASE ) ;
BIND_ENUM_CONSTANT ( SEARCH_WHOLE_WORDS ) ;
BIND_ENUM_CONSTANT ( SEARCH_BACKWARDS ) ;
ClassDB : : bind_method ( D_METHOD ( " set_search_text " , " search_text " ) , & TextEdit : : set_search_text ) ;
ClassDB : : bind_method ( D_METHOD ( " set_search_flags " , " flags " ) , & TextEdit : : set_search_flags ) ;
2024-01-22 23:26:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " search " , " text " , " flags " , " from_line " , " from_column " ) , & TextEdit : : search ) ;
2021-07-10 10:41:38 +00:00
/* Tooltip */
2021-11-07 17:26:15 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_tooltip_request_func " , " callback " ) , & TextEdit : : set_tooltip_request_func ) ;
2021-07-10 10:41:38 +00:00
/* Mouse */
ClassDB : : bind_method ( D_METHOD ( " get_local_mouse_pos " ) , & TextEdit : : get_local_mouse_pos ) ;
ClassDB : : bind_method ( D_METHOD ( " get_word_at_pos " , " position " ) , & TextEdit : : get_word_at_pos ) ;
2021-10-31 18:34:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_line_column_at_pos " , " position " , " allow_out_of_bounds " ) , & TextEdit : : get_line_column_at_pos , DEFVAL ( true ) ) ;
2021-11-18 15:55:43 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_pos_at_line_column " , " line " , " column " ) , & TextEdit : : get_pos_at_line_column ) ;
ClassDB : : bind_method ( D_METHOD ( " get_rect_at_line_column " , " line " , " column " ) , & TextEdit : : get_rect_at_line_column ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_minimap_line_at_pos " , " position " ) , & TextEdit : : get_minimap_line_at_pos ) ;
ClassDB : : bind_method ( D_METHOD ( " is_dragging_cursor " ) , & TextEdit : : is_dragging_cursor ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_mouse_over_selection " , " edges " , " caret_index " ) , & TextEdit : : is_mouse_over_selection , DEFVAL ( - 1 ) ) ;
2021-07-10 10:41:38 +00:00
2021-07-08 17:35:56 +00:00
/* Caret. */
BIND_ENUM_CONSTANT ( CARET_TYPE_LINE ) ;
BIND_ENUM_CONSTANT ( CARET_TYPE_BLOCK ) ;
ClassDB : : bind_method ( D_METHOD ( " set_caret_type " , " type " ) , & TextEdit : : set_caret_type ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_type " ) , & TextEdit : : get_caret_type ) ;
ClassDB : : bind_method ( D_METHOD ( " set_caret_blink_enabled " , " enable " ) , & TextEdit : : set_caret_blink_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_caret_blink_enabled " ) , & TextEdit : : is_caret_blink_enabled ) ;
2022-08-13 12:52:35 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_caret_blink_interval " , " interval " ) , & TextEdit : : set_caret_blink_interval ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_blink_interval " ) , & TextEdit : : get_caret_blink_interval ) ;
2021-07-08 17:35:56 +00:00
2023-02-07 23:02:48 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_caret_when_editable_disabled " , " enable " ) , & TextEdit : : set_draw_caret_when_editable_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_drawing_caret_when_editable_disabled " ) , & TextEdit : : is_drawing_caret_when_editable_disabled ) ;
2021-07-08 17:35:56 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_move_caret_on_right_click_enabled " , " enable " ) , & TextEdit : : set_move_caret_on_right_click_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_move_caret_on_right_click_enabled " ) , & TextEdit : : is_move_caret_on_right_click_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_caret_mid_grapheme_enabled " , " enabled " ) , & TextEdit : : set_caret_mid_grapheme_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_caret_mid_grapheme_enabled " ) , & TextEdit : : is_caret_mid_grapheme_enabled ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_multiple_carets_enabled " , " enabled " ) , & TextEdit : : set_multiple_carets_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_multiple_carets_enabled " ) , & TextEdit : : is_multiple_carets_enabled ) ;
2024-01-22 23:26:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_caret " , " line " , " column " ) , & TextEdit : : add_caret ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_caret " , " caret " ) , & TextEdit : : remove_caret ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_secondary_carets " ) , & TextEdit : : remove_secondary_carets ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_count " ) , & TextEdit : : get_caret_count ) ;
2022-10-09 15:07:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_caret_at_carets " , " below " ) , & TextEdit : : add_caret_at_carets ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_sorted_carets " , " include_ignored_carets " ) , & TextEdit : : get_sorted_carets , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " collapse_carets " , " from_line " , " from_column " , " to_line " , " to_column " , " inclusive " ) , & TextEdit : : collapse_carets , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " merge_overlapping_carets " ) , & TextEdit : : merge_overlapping_carets ) ;
ClassDB : : bind_method ( D_METHOD ( " begin_multicaret_edit " ) , & TextEdit : : begin_multicaret_edit ) ;
ClassDB : : bind_method ( D_METHOD ( " end_multicaret_edit " ) , & TextEdit : : end_multicaret_edit ) ;
ClassDB : : bind_method ( D_METHOD ( " is_in_mulitcaret_edit " ) , & TextEdit : : is_in_mulitcaret_edit ) ;
ClassDB : : bind_method ( D_METHOD ( " multicaret_edit_ignore_caret " , " caret_index " ) , & TextEdit : : multicaret_edit_ignore_caret ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_caret_visible " , " caret_index " ) , & TextEdit : : is_caret_visible , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_draw_pos " , " caret_index " ) , & TextEdit : : get_caret_draw_pos , DEFVAL ( 0 ) ) ;
2021-07-08 17:35:56 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_caret_line " , " line " , " adjust_viewport " , " can_be_hidden " , " wrap_index " , " caret_index " ) , & TextEdit : : set_caret_line , DEFVAL ( true ) , DEFVAL ( true ) , DEFVAL ( 0 ) , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_line " , " caret_index " ) , & TextEdit : : get_caret_line , DEFVAL ( 0 ) ) ;
2021-07-08 17:35:56 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_caret_column " , " column " , " adjust_viewport " , " caret_index " ) , & TextEdit : : set_caret_column , DEFVAL ( true ) , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_column " , " caret_index " ) , & TextEdit : : get_caret_column , DEFVAL ( 0 ) ) ;
2021-07-08 17:35:56 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_caret_wrap_index " , " caret_index " ) , & TextEdit : : get_caret_wrap_index , DEFVAL ( 0 ) ) ;
2021-07-08 17:35:56 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_word_under_caret " , " caret_index " ) , & TextEdit : : get_word_under_caret , DEFVAL ( - 1 ) ) ;
2021-07-10 10:41:38 +00:00
2024-05-29 11:06:43 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_use_default_word_separators " , " enabled " ) , & TextEdit : : set_use_default_word_separators ) ;
ClassDB : : bind_method ( D_METHOD ( " is_default_word_separators_enabled " ) , & TextEdit : : is_default_word_separators_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_use_custom_word_separators " , " enabled " ) , & TextEdit : : set_use_custom_word_separators ) ;
ClassDB : : bind_method ( D_METHOD ( " is_custom_word_separators_enabled " ) , & TextEdit : : is_custom_word_separators_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_custom_word_separators " , " custom_word_separators " ) , & TextEdit : : set_custom_word_separators ) ;
ClassDB : : bind_method ( D_METHOD ( " get_custom_word_separators " ) , & TextEdit : : get_custom_word_separators ) ;
2021-07-09 11:42:55 +00:00
/* Selection. */
BIND_ENUM_CONSTANT ( SELECTION_MODE_NONE ) ;
BIND_ENUM_CONSTANT ( SELECTION_MODE_SHIFT ) ;
BIND_ENUM_CONSTANT ( SELECTION_MODE_POINTER ) ;
BIND_ENUM_CONSTANT ( SELECTION_MODE_WORD ) ;
BIND_ENUM_CONSTANT ( SELECTION_MODE_LINE ) ;
ClassDB : : bind_method ( D_METHOD ( " set_selecting_enabled " , " enable " ) , & TextEdit : : set_selecting_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_selecting_enabled " ) , & TextEdit : : is_selecting_enabled ) ;
2021-10-21 21:02:46 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_deselect_on_focus_loss_enabled " , " enable " ) , & TextEdit : : set_deselect_on_focus_loss_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_deselect_on_focus_loss_enabled " ) , & TextEdit : : is_deselect_on_focus_loss_enabled ) ;
2022-06-19 14:55:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_drag_and_drop_selection_enabled " , " enable " ) , & TextEdit : : set_drag_and_drop_selection_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_drag_and_drop_selection_enabled " ) , & TextEdit : : is_drag_and_drop_selection_enabled ) ;
2024-01-22 23:26:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_selection_mode " , " mode " ) , & TextEdit : : set_selection_mode ) ;
2021-07-09 11:42:55 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_selection_mode " ) , & TextEdit : : get_selection_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " select_all " ) , & TextEdit : : select_all ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " select_word_under_caret " , " caret_index " ) , & TextEdit : : select_word_under_caret , DEFVAL ( - 1 ) ) ;
2022-10-19 17:35:14 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_selection_for_next_occurrence " ) , & TextEdit : : add_selection_for_next_occurrence ) ;
2024-02-02 22:10:55 +00:00
ClassDB : : bind_method ( D_METHOD ( " skip_selection_for_next_occurrence " ) , & TextEdit : : skip_selection_for_next_occurrence ) ;
2024-01-22 23:26:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " select " , " origin_line " , " origin_column " , " caret_line " , " caret_column " , " caret_index " ) , & TextEdit : : select , DEFVAL ( 0 ) ) ;
2021-07-09 11:42:55 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_selection " , " caret_index " ) , & TextEdit : : has_selection , DEFVAL ( - 1 ) ) ;
2021-07-09 11:42:55 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_selected_text " , " caret_index " ) , & TextEdit : : get_selected_text , DEFVAL ( - 1 ) ) ;
2024-01-22 23:27:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_selection_at_line_column " , " line " , " column " , " include_edges " , " only_selections " ) , & TextEdit : : get_selection_at_line_column , DEFVAL ( true ) , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_ranges_from_carets " , " only_selections " , " merge_adjacent " ) , & TextEdit : : get_line_ranges_from_carets_typed_array , DEFVAL ( false ) , DEFVAL ( true ) ) ;
2021-07-09 11:42:55 +00:00
2024-01-22 23:26:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_selection_origin_line " , " caret_index " ) , & TextEdit : : get_selection_origin_line , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_origin_column " , " caret_index " ) , & TextEdit : : get_selection_origin_column , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_selection_origin_line " , " line " , " can_be_hidden " , " wrap_index " , " caret_index " ) , & TextEdit : : set_selection_origin_line , DEFVAL ( true ) , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_selection_origin_column " , " column " , " caret_index " ) , & TextEdit : : set_selection_origin_column , DEFVAL ( 0 ) ) ;
2021-07-09 11:42:55 +00:00
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_selection_from_line " , " caret_index " ) , & TextEdit : : get_selection_from_line , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_from_column " , " caret_index " ) , & TextEdit : : get_selection_from_column , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_to_line " , " caret_index " ) , & TextEdit : : get_selection_to_line , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_to_column " , " caret_index " ) , & TextEdit : : get_selection_to_column , DEFVAL ( 0 ) ) ;
2021-07-09 11:42:55 +00:00
2024-01-22 23:26:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_caret_after_selection_origin " , " caret_index " ) , & TextEdit : : is_caret_after_selection_origin , DEFVAL ( 0 ) ) ;
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " deselect " , " caret_index " ) , & TextEdit : : deselect , DEFVAL ( - 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " delete_selection " , " caret_index " ) , & TextEdit : : delete_selection , DEFVAL ( - 1 ) ) ;
2021-07-09 11:42:55 +00:00
2021-11-18 15:55:43 +00:00
/* Line wrapping. */
2021-07-09 10:52:49 +00:00
BIND_ENUM_CONSTANT ( LINE_WRAPPING_NONE ) ;
BIND_ENUM_CONSTANT ( LINE_WRAPPING_BOUNDARY ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_wrapping_mode " , " mode " ) , & TextEdit : : set_line_wrapping_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_wrapping_mode " ) , & TextEdit : : get_line_wrapping_mode ) ;
2023-03-12 11:21:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_autowrap_mode " , " autowrap_mode " ) , & TextEdit : : set_autowrap_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_autowrap_mode " ) , & TextEdit : : get_autowrap_mode ) ;
2021-07-09 10:52:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_line_wrapped " , " line " ) , & TextEdit : : is_line_wrapped ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_wrap_count " , " line " ) , & TextEdit : : get_line_wrap_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_wrap_index_at_column " , " line " , " column " ) , & TextEdit : : get_line_wrap_index_at_column ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_wrapped_text " , " line " ) , & TextEdit : : get_line_wrapped_text ) ;
2021-07-09 16:27:09 +00:00
/* Viewport. */
Fix various typos
Found via ` codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,expct,fave,findn,gird,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint,varn`
Update editor/import/resource_importer_layered_texture.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update doc/classes/TileSetScenesCollectionSource.xml
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/gui/graph_edit.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/gui/rich_text_label.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Revert previously committed change
2022-01-02 06:03:58 +00:00
// Scrolling.
2022-06-20 23:19:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_smooth_scroll_enabled " , " enable " ) , & TextEdit : : set_smooth_scroll_enabled ) ;
2021-07-09 16:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_smooth_scroll_enabled " ) , & TextEdit : : is_smooth_scroll_enabled ) ;
2022-09-28 04:55:06 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll_bar " ) , & TextEdit : : get_v_scroll_bar ) ;
ClassDB : : bind_method ( D_METHOD ( " get_h_scroll_bar " ) , & TextEdit : : get_h_scroll_bar ) ;
2021-07-09 16:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_v_scroll " , " value " ) , & TextEdit : : set_v_scroll ) ;
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll " ) , & TextEdit : : get_v_scroll ) ;
ClassDB : : bind_method ( D_METHOD ( " set_h_scroll " , " value " ) , & TextEdit : : set_h_scroll ) ;
ClassDB : : bind_method ( D_METHOD ( " get_h_scroll " ) , & TextEdit : : get_h_scroll ) ;
ClassDB : : bind_method ( D_METHOD ( " set_scroll_past_end_of_file_enabled " , " enable " ) , & TextEdit : : set_scroll_past_end_of_file_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_scroll_past_end_of_file_enabled " ) , & TextEdit : : is_scroll_past_end_of_file_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_v_scroll_speed " , " speed " ) , & TextEdit : : set_v_scroll_speed ) ;
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll_speed " ) , & TextEdit : : get_v_scroll_speed ) ;
2022-08-08 12:18:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_fit_content_height_enabled " , " enabled " ) , & TextEdit : : set_fit_content_height_enabled ) ;
2022-06-20 23:19:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_fit_content_height_enabled " ) , & TextEdit : : is_fit_content_height_enabled ) ;
2021-07-09 16:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_scroll_pos_for_line " , " line " , " wrap_index " ) , & TextEdit : : get_scroll_pos_for_line , DEFVAL ( 0 ) ) ;
// Visible lines.
ClassDB : : bind_method ( D_METHOD ( " set_line_as_first_visible " , " line " , " wrap_index " ) , & TextEdit : : set_line_as_first_visible , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_first_visible_line " ) , & TextEdit : : get_first_visible_line ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_as_center_visible " , " line " , " wrap_index " ) , & TextEdit : : set_line_as_center_visible , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_as_last_visible " , " line " , " wrap_index " ) , & TextEdit : : set_line_as_last_visible , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_last_full_visible_line " ) , & TextEdit : : get_last_full_visible_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_last_full_visible_line_wrap_index " ) , & TextEdit : : get_last_full_visible_line_wrap_index ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_line_count " ) , & TextEdit : : get_visible_line_count ) ;
2021-10-31 18:34:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_visible_line_count_in_range " , " from_line " , " to_line " ) , & TextEdit : : get_visible_line_count_in_range ) ;
2021-07-09 16:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_total_visible_line_count " ) , & TextEdit : : get_total_visible_line_count ) ;
// Auto adjust
2022-06-03 16:47:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " adjust_viewport_to_caret " , " caret_index " ) , & TextEdit : : adjust_viewport_to_caret , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " center_viewport_to_caret " , " caret_index " ) , & TextEdit : : center_viewport_to_caret , DEFVAL ( 0 ) ) ;
2021-07-09 16:27:09 +00:00
// Minimap
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_minimap " , " enabled " ) , & TextEdit : : set_draw_minimap ) ;
2021-07-09 16:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_drawing_minimap " ) , & TextEdit : : is_drawing_minimap ) ;
ClassDB : : bind_method ( D_METHOD ( " set_minimap_width " , " width " ) , & TextEdit : : set_minimap_width ) ;
ClassDB : : bind_method ( D_METHOD ( " get_minimap_width " ) , & TextEdit : : get_minimap_width ) ;
ClassDB : : bind_method ( D_METHOD ( " get_minimap_visible_lines " ) , & TextEdit : : get_minimap_visible_lines ) ;
2020-07-25 00:15:23 +00:00
/* Gutters. */
BIND_ENUM_CONSTANT ( GUTTER_TYPE_STRING ) ;
2021-07-19 08:16:00 +00:00
BIND_ENUM_CONSTANT ( GUTTER_TYPE_ICON ) ;
BIND_ENUM_CONSTANT ( GUTTER_TYPE_CUSTOM ) ;
2020-07-25 00:15:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_gutter " , " at " ) , & TextEdit : : add_gutter , DEFVAL ( - 1 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_gutter " , " gutter " ) , & TextEdit : : remove_gutter ) ;
ClassDB : : bind_method ( D_METHOD ( " get_gutter_count " ) , & TextEdit : : get_gutter_count ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gutter_name " , " gutter " , " name " ) , & TextEdit : : set_gutter_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_gutter_name " , " gutter " ) , & TextEdit : : get_gutter_name ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gutter_type " , " gutter " , " type " ) , & TextEdit : : set_gutter_type ) ;
ClassDB : : bind_method ( D_METHOD ( " get_gutter_type " , " gutter " ) , & TextEdit : : get_gutter_type ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gutter_width " , " gutter " , " width " ) , & TextEdit : : set_gutter_width ) ;
ClassDB : : bind_method ( D_METHOD ( " get_gutter_width " , " gutter " ) , & TextEdit : : get_gutter_width ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gutter_draw " , " gutter " , " draw " ) , & TextEdit : : set_gutter_draw ) ;
ClassDB : : bind_method ( D_METHOD ( " is_gutter_drawn " , " gutter " ) , & TextEdit : : is_gutter_drawn ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gutter_clickable " , " gutter " , " clickable " ) , & TextEdit : : set_gutter_clickable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_gutter_clickable " , " gutter " ) , & TextEdit : : is_gutter_clickable ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gutter_overwritable " , " gutter " , " overwritable " ) , & TextEdit : : set_gutter_overwritable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_gutter_overwritable " , " gutter " ) , & TextEdit : : is_gutter_overwritable ) ;
2021-06-15 14:05:01 +00:00
ClassDB : : bind_method ( D_METHOD ( " merge_gutters " , " from_line " , " to_line " ) , & TextEdit : : merge_gutters ) ;
2021-11-07 17:26:15 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_gutter_custom_draw " , " column " , " draw_callback " ) , & TextEdit : : set_gutter_custom_draw ) ;
2021-06-30 17:03:32 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_total_gutter_width " ) , & TextEdit : : get_total_gutter_width ) ;
2020-07-25 00:15:23 +00:00
// Line gutters.
ClassDB : : bind_method ( D_METHOD ( " set_line_gutter_metadata " , " line " , " gutter " , " metadata " ) , & TextEdit : : set_line_gutter_metadata ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_gutter_metadata " , " line " , " gutter " ) , & TextEdit : : get_line_gutter_metadata ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_gutter_text " , " line " , " gutter " , " text " ) , & TextEdit : : set_line_gutter_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_gutter_text " , " line " , " gutter " ) , & TextEdit : : get_line_gutter_text ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_gutter_icon " , " line " , " gutter " , " icon " ) , & TextEdit : : set_line_gutter_icon ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_gutter_icon " , " line " , " gutter " ) , & TextEdit : : get_line_gutter_icon ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_gutter_item_color " , " line " , " gutter " , " color " ) , & TextEdit : : set_line_gutter_item_color ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_gutter_item_color " , " line " , " gutter " ) , & TextEdit : : get_line_gutter_item_color ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_gutter_clickable " , " line " , " gutter " , " clickable " ) , & TextEdit : : set_line_gutter_clickable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_line_gutter_clickable " , " line " , " gutter " ) , & TextEdit : : is_line_gutter_clickable ) ;
2020-09-10 20:25:00 +00:00
// Line style
ClassDB : : bind_method ( D_METHOD ( " set_line_background_color " , " line " , " color " ) , & TextEdit : : set_line_background_color ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line_background_color " , " line " ) , & TextEdit : : get_line_background_color ) ;
2021-07-10 10:41:38 +00:00
/* Syntax Highlighting. */
ClassDB : : bind_method ( D_METHOD ( " set_syntax_highlighter " , " syntax_highlighter " ) , & TextEdit : : set_syntax_highlighter ) ;
ClassDB : : bind_method ( D_METHOD ( " get_syntax_highlighter " ) , & TextEdit : : get_syntax_highlighter ) ;
/* Visual. */
2017-10-22 12:38:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_highlight_current_line " , " enabled " ) , & TextEdit : : set_highlight_current_line ) ;
2021-07-09 16:27:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_highlight_current_line_enabled " ) , & TextEdit : : is_highlight_current_line_enabled ) ;
2017-08-19 14:23:45 +00:00
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_highlight_all_occurrences " , " enabled " ) , & TextEdit : : set_highlight_all_occurrences ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_highlight_all_occurrences_enabled " ) , & TextEdit : : is_highlight_all_occurrences_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " get_draw_control_chars " ) , & TextEdit : : get_draw_control_chars ) ;
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_control_chars " , " enabled " ) , & TextEdit : : set_draw_control_chars ) ;
2021-07-10 10:41:38 +00:00
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_tabs " , " enabled " ) , & TextEdit : : set_draw_tabs ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_drawing_tabs " ) , & TextEdit : : is_drawing_tabs ) ;
2021-10-08 21:26:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_spaces " , " enabled " ) , & TextEdit : : set_draw_spaces ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_drawing_spaces " ) , & TextEdit : : is_drawing_spaces ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_menu " ) , & TextEdit : : get_menu ) ;
2021-07-16 21:36:05 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_menu_visible " ) , & TextEdit : : is_menu_visible ) ;
2021-07-10 10:41:38 +00:00
ClassDB : : bind_method ( D_METHOD ( " menu_option " , " option " ) , & TextEdit : : menu_option ) ;
2016-03-08 23:00:52 +00:00
2024-01-22 23:27:19 +00:00
/* Deprecated */
# ifndef DISABLE_DEPRECATED
ClassDB : : bind_method ( D_METHOD ( " adjust_carets_after_edit " , " caret " , " from_line " , " from_col " , " to_line " , " to_col " ) , & TextEdit : : adjust_carets_after_edit ) ;
ClassDB : : bind_method ( D_METHOD ( " get_caret_index_edit_order " ) , & TextEdit : : get_caret_index_edit_order ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_line " , " caret_index " ) , & TextEdit : : get_selection_line , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_column " , " caret_index " ) , & TextEdit : : get_selection_column , DEFVAL ( 0 ) ) ;
# endif
2021-07-10 13:01:25 +00:00
/* Inspector */
2017-11-12 15:10:26 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2022-01-23 20:07:35 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " placeholder_text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_placeholder " , " get_placeholder " ) ;
2021-07-10 13:01:25 +00:00
2021-07-09 13:05:52 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " editable " ) , " set_editable " , " is_editable " ) ;
2017-11-09 20:46:29 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " context_menu_enabled " ) , " set_context_menu_enabled " , " is_context_menu_enabled " ) ;
2019-08-08 00:09:46 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " shortcut_keys_enabled " ) , " set_shortcut_keys_enabled " , " is_shortcut_keys_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " selecting_enabled " ) , " set_selecting_enabled " , " is_selecting_enabled " ) ;
2021-10-21 21:02:46 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " deselect_on_focus_loss_enabled " ) , " set_deselect_on_focus_loss_enabled " , " is_deselect_on_focus_loss_enabled " ) ;
2022-06-19 14:55:13 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " drag_and_drop_selection_enabled " ) , " set_drag_and_drop_selection_enabled " , " is_drag_and_drop_selection_enabled " ) ;
2021-07-10 13:01:25 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " virtual_keyboard_enabled " ) , " set_virtual_keyboard_enabled " , " is_virtual_keyboard_enabled " ) ;
2021-10-12 07:43:50 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " middle_mouse_paste_enabled " ) , " set_middle_mouse_paste_enabled " , " is_middle_mouse_paste_enabled " ) ;
2021-07-09 10:52:49 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " wrap_mode " , PROPERTY_HINT_ENUM , " None,Boundary " ) , " set_line_wrapping_mode " , " get_line_wrapping_mode " ) ;
2023-03-12 11:21:00 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " autowrap_mode " , PROPERTY_HINT_ENUM , " Arbitrary:1,Word:2,Word (Smart):3 " ) , " set_autowrap_mode " , " get_autowrap_mode " ) ;
2024-02-19 11:41:12 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " indent_wrapped_lines " ) , " set_indent_wrapped_lines " , " is_indent_wrapped_lines " ) ;
2021-07-10 13:01:25 +00:00
ADD_GROUP ( " Scroll " , " scroll_ " ) ;
2022-06-20 23:19:54 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_smooth " ) , " set_smooth_scroll_enabled " , " is_smooth_scroll_enabled " ) ;
2024-07-26 21:16:59 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " scroll_v_scroll_speed " , PROPERTY_HINT_NONE , " suffix:lines/s " ) , " set_v_scroll_speed " , " get_v_scroll_speed " ) ;
2021-07-10 13:01:25 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_past_end_of_file " ) , " set_scroll_past_end_of_file_enabled " , " is_scroll_past_end_of_file_enabled " ) ;
2024-07-26 21:16:59 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " scroll_vertical " , PROPERTY_HINT_NONE , " suffix:lines " ) , " set_v_scroll " , " get_v_scroll " ) ;
2022-05-20 05:24:41 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_horizontal " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_h_scroll " , " get_h_scroll " ) ;
2022-06-20 23:19:54 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_fit_content_height " ) , " set_fit_content_height_enabled " , " is_fit_content_height_enabled " ) ;
2021-07-10 13:01:25 +00:00
2019-08-11 18:31:19 +00:00
ADD_GROUP ( " Minimap " , " minimap_ " ) ;
2021-10-08 21:26:13 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " minimap_draw " ) , " set_draw_minimap " , " is_drawing_minimap " ) ;
2022-05-20 05:24:41 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " minimap_width " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_minimap_width " , " get_minimap_width " ) ;
2019-08-11 18:31:19 +00:00
2017-01-04 04:16:14 +00:00
ADD_GROUP ( " Caret " , " caret_ " ) ;
2021-07-08 17:35:56 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " caret_type " , PROPERTY_HINT_ENUM , " Line,Block " ) , " set_caret_type " , " get_caret_type " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_blink " ) , " set_caret_blink_enabled " , " is_caret_blink_enabled " ) ;
2022-08-13 12:52:35 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " caret_blink_interval " , PROPERTY_HINT_RANGE , " 0.1,10,0.01,suffix:s " ) , " set_caret_blink_interval " , " get_caret_blink_interval " ) ;
2023-02-07 23:02:48 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_draw_when_editable_disabled " ) , " set_draw_caret_when_editable_disabled " , " is_drawing_caret_when_editable_disabled " ) ;
2021-07-08 17:35:56 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_move_on_right_click " ) , " set_move_caret_on_right_click_enabled " , " is_move_caret_on_right_click_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_mid_grapheme " ) , " set_caret_mid_grapheme_enabled " , " is_caret_mid_grapheme_enabled " ) ;
2022-06-03 16:47:37 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_multiple " ) , " set_multiple_carets_enabled " , " is_multiple_carets_enabled " ) ;
2020-09-18 07:36:10 +00:00
2024-05-29 11:06:43 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_default_word_separators " ) , " set_use_default_word_separators " , " is_default_word_separators_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_custom_word_separators " ) , " set_use_custom_word_separators " , " is_custom_word_separators_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " custom_word_separators " ) , " set_custom_word_separators " , " get_custom_word_separators " ) ;
2020-09-18 07:36:10 +00:00
2023-10-04 10:14:59 +00:00
ADD_GROUP ( " Highlighting " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " syntax_highlighter " , PROPERTY_HINT_RESOURCE_TYPE , " SyntaxHighlighter " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE ) , " set_syntax_highlighter " , " get_syntax_highlighter " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " highlight_all_occurrences " ) , " set_highlight_all_occurrences " , " is_highlight_all_occurrences_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " highlight_current_line " ) , " set_highlight_current_line " , " is_highlight_current_line_enabled " ) ;
ADD_GROUP ( " Visual Whitespace " , " draw_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_control_chars " ) , " set_draw_control_chars " , " get_draw_control_chars " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_tabs " ) , " set_draw_tabs " , " is_drawing_tabs " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_spaces " ) , " set_draw_spaces " , " is_drawing_spaces " ) ;
2022-05-09 09:47:10 +00:00
ADD_GROUP ( " BiDi " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " text_direction " , PROPERTY_HINT_ENUM , " Auto,Left-to-Right,Right-to-Left,Inherited " ) , " set_text_direction " , " get_text_direction " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " language " , PROPERTY_HINT_LOCALE_ID , " " ) , " set_language " , " get_language " ) ;
2020-09-18 07:36:10 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " structured_text_bidi_override " , PROPERTY_HINT_ENUM , " Default,URI,File,Email,List,None,Custom " ) , " set_structured_text_bidi_override " , " get_structured_text_bidi_override " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " structured_text_bidi_override_options " ) , " set_structured_text_bidi_override_options " , " get_structured_text_bidi_override_options " ) ;
2016-03-08 23:00:52 +00:00
2021-07-08 17:35:56 +00:00
/* Signals */
2021-07-10 13:01:25 +00:00
/* Core. */
2021-07-11 16:07:29 +00:00
ADD_SIGNAL ( MethodInfo ( " text_set " ) ) ;
2021-07-10 13:01:25 +00:00
ADD_SIGNAL ( MethodInfo ( " text_changed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " lines_edited_from " , PropertyInfo ( Variant : : INT , " from_line " ) , PropertyInfo ( Variant : : INT , " to_line " ) ) ) ;
2021-07-08 17:35:56 +00:00
/* Caret. */
ADD_SIGNAL ( MethodInfo ( " caret_changed " ) ) ;
2021-07-10 13:01:25 +00:00
/* Gutters. */
2020-07-25 00:15:23 +00:00
ADD_SIGNAL ( MethodInfo ( " gutter_clicked " , PropertyInfo ( Variant : : INT , " line " ) , PropertyInfo ( Variant : : INT , " gutter " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " gutter_added " ) ) ;
ADD_SIGNAL ( MethodInfo ( " gutter_removed " ) ) ;
2016-05-16 23:25:17 +00:00
2024-01-22 23:27:19 +00:00
// Theme items
2023-09-08 19:00:10 +00:00
/* Search */
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , search_result_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , search_result_border_color ) ;
/* Caret */
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , TextEdit , caret_width ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , caret_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , caret_background_color ) ;
/* Selection */
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , font_selected_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , selection_color ) ;
/* Other visuals */
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , TextEdit , style_normal , " normal " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , TextEdit , style_focus , " focus " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , TextEdit , style_readonly , " read_only " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_ICON , TextEdit , tab_icon , " tab " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_ICON , TextEdit , space_icon , " space " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT , TextEdit , font ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT_SIZE , TextEdit , font_size ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , font_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , font_readonly_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , font_placeholder_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , TextEdit , outline_size ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_COLOR , TextEdit , outline_color , " font_outline_color " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , TextEdit , line_spacing ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , background_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , current_line_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , TextEdit , word_highlighted_color ) ;
2021-07-10 13:01:25 +00:00
/* Settings. */
2022-11-08 18:53:22 +00:00
GLOBAL_DEF ( PropertyInfo ( Variant : : FLOAT , " gui/timers/text_edit_idle_detect_sec " , PROPERTY_HINT_RANGE , " 0,10,0.01,or_greater " ) , 3 ) ;
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " gui/common/text_edit_undo_stack_max_size " , PROPERTY_HINT_RANGE , " 0,10000,1,or_greater " ) , 1024 ) ;
2014-02-10 01:10:30 +00:00
}
2021-07-09 12:07:10 +00:00
/* Internal API for CodeEdit. */
// Line hiding.
void TextEdit : : _set_hiding_enabled ( bool p_enabled ) {
2022-03-16 07:50:48 +00:00
if ( hiding_enabled = = p_enabled ) {
return ;
}
2021-07-09 12:07:10 +00:00
if ( ! p_enabled ) {
_unhide_all_lines ( ) ;
}
hiding_enabled = p_enabled ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-09 12:07:10 +00:00
}
bool TextEdit : : _is_hiding_enabled ( ) const {
return hiding_enabled ;
}
bool TextEdit : : _is_line_hidden ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
return text . is_hidden ( p_line ) ;
}
void TextEdit : : _unhide_all_lines ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
text . set_hidden ( i , false ) ;
}
_update_scrollbars ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-09 12:07:10 +00:00
}
2024-01-22 23:26:16 +00:00
void TextEdit : : _unhide_carets ( ) {
// Override for functionality.
}
2021-07-09 12:07:10 +00:00
void TextEdit : : _set_line_as_hidden ( int p_line , bool p_hidden ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( text . is_hidden ( p_line ) = = p_hidden ) {
return ;
}
2021-07-09 12:07:10 +00:00
if ( _is_hiding_enabled ( ) | | ! p_hidden ) {
text . set_hidden ( p_line , p_hidden ) ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-09 12:07:10 +00:00
}
// Symbol lookup.
void TextEdit : : _set_symbol_lookup_word ( const String & p_symbol ) {
2022-03-16 07:50:48 +00:00
if ( lookup_symbol_word = = p_symbol ) {
return ;
}
2021-07-09 12:07:10 +00:00
lookup_symbol_word = p_symbol ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-09 12:07:10 +00:00
}
2021-07-08 15:48:58 +00:00
/* Text manipulation */
// Overridable actions
2022-06-03 16:47:37 +00:00
void TextEdit : : _handle_unicode_input_internal ( const uint32_t p_unicode , int p_caret ) {
2024-01-22 23:27:19 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2021-07-08 15:48:58 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
start_action ( EditAction : : ACTION_TYPING ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( p_caret = = - 1 & & multicaret_edit_ignore_caret ( i ) ) {
continue ;
}
2022-06-03 16:47:37 +00:00
if ( p_caret ! = - 1 & & p_caret ! = i ) {
continue ;
2021-07-08 15:48:58 +00:00
}
2023-03-08 22:00:27 +00:00
// Remove the old character if in insert mode and no selection.
2022-06-03 16:47:37 +00:00
if ( overtype_mode & & ! has_selection ( i ) ) {
2023-03-08 22:00:27 +00:00
// Make sure we don't try and remove empty space.
2022-06-03 16:47:37 +00:00
int cl = get_caret_line ( i ) ;
int cc = get_caret_column ( i ) ;
if ( cc < get_line ( cl ) . length ( ) ) {
_remove_text ( cl , cc , cl , cc + 1 ) ;
}
}
2021-07-08 15:48:58 +00:00
2022-06-03 16:47:37 +00:00
const char32_t chr [ 2 ] = { ( char32_t ) p_unicode , 0 } ;
insert_text_at_caret ( chr , i ) ;
2021-07-08 15:48:58 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2022-06-03 16:47:37 +00:00
end_action ( ) ;
2021-07-08 15:48:58 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : _backspace_internal ( int p_caret ) {
2024-01-22 23:26:16 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2021-07-08 15:48:58 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
if ( has_selection ( p_caret ) ) {
delete_selection ( p_caret ) ;
2021-08-16 23:30:14 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
if ( p_caret = = - 1 & & multicaret_edit_ignore_caret ( i ) ) {
continue ;
}
2022-06-03 16:47:37 +00:00
if ( p_caret ! = - 1 & & p_caret ! = i ) {
continue ;
}
2024-01-22 23:27:19 +00:00
int to_line = get_caret_line ( i ) ;
int to_column = get_caret_column ( i ) ;
2021-07-08 15:48:58 +00:00
2024-01-22 23:27:19 +00:00
if ( to_column = = 0 & & to_line = = 0 ) {
2022-06-03 16:47:37 +00:00
continue ;
}
2021-07-08 15:48:58 +00:00
2024-01-22 23:27:19 +00:00
int from_line = to_column > 0 ? to_line : to_line - 1 ;
int from_column = to_column > 0 ? ( to_column - 1 ) : ( text [ to_line - 1 ] . length ( ) ) ;
2021-07-08 15:48:58 +00:00
2024-01-22 23:27:19 +00:00
merge_gutters ( from_line , to_line ) ;
2021-07-08 15:48:58 +00:00
2024-01-22 23:27:19 +00:00
_remove_text ( from_line , from_column , to_line , to_column ) ;
collapse_carets ( from_line , from_column , to_line , to_column ) ;
_offset_carets_after ( to_line , to_column , from_line , from_column ) ;
2022-06-03 16:47:37 +00:00
2024-01-22 23:27:19 +00:00
set_caret_line ( from_line , false , true , - 1 , i ) ;
set_caret_column ( from_column , i = = 0 , i ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2022-06-03 16:47:37 +00:00
end_complex_operation ( ) ;
2021-07-08 15:48:58 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : _cut_internal ( int p_caret ) {
2024-01-22 23:27:19 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
_copy_internal ( p_caret ) ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2021-07-08 15:48:58 +00:00
return ;
}
2022-06-03 16:47:37 +00:00
if ( has_selection ( p_caret ) ) {
delete_selection ( p_caret ) ;
2021-07-08 15:48:58 +00:00
return ;
}
2024-01-22 23:27:19 +00:00
// Remove full lines.
2022-06-03 16:47:37 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
Vector < Point2i > line_ranges ;
if ( p_caret = = - 1 ) {
line_ranges = get_line_ranges_from_carets ( ) ;
} else {
line_ranges . push_back ( Point2i ( get_caret_line ( p_caret ) , get_caret_line ( p_caret ) ) ) ;
2021-10-02 09:23:36 +00:00
}
2024-01-22 23:27:19 +00:00
int line_offset = 0 ;
for ( Point2i line_range : line_ranges ) {
// Preserve carets on the last line.
remove_line_at ( line_range . y + line_offset ) ;
if ( line_range . x ! = line_range . y ) {
remove_text ( line_range . x + line_offset , 0 , line_range . y + line_offset , 0 ) ;
}
line_offset + = line_range . x - line_range . y - 1 ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2022-06-03 16:47:37 +00:00
end_complex_operation ( ) ;
2021-07-08 15:48:58 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : _copy_internal ( int p_caret ) {
2024-01-22 23:27:19 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2022-06-03 16:47:37 +00:00
if ( has_selection ( p_caret ) ) {
DisplayServer : : get_singleton ( ) - > clipboard_set ( get_selected_text ( p_caret ) ) ;
2021-07-08 15:48:58 +00:00
cut_copy_line = " " ;
return ;
}
2024-01-22 23:27:19 +00:00
// Copy full lines.
2022-06-03 16:47:37 +00:00
StringBuilder clipboard ;
2024-01-22 23:27:19 +00:00
Vector < Point2i > line_ranges ;
if ( p_caret = = - 1 ) {
// When there are multiple carets on a line, only copy it once.
line_ranges = get_line_ranges_from_carets ( false , true ) ;
} else {
line_ranges . push_back ( Point2i ( get_caret_line ( p_caret ) , get_caret_line ( p_caret ) ) ) ;
}
for ( Point2i line_range : line_ranges ) {
for ( int i = line_range . x ; i < = line_range . y ; i + + ) {
if ( text [ i ] . length ( ) ! = 0 ) {
clipboard + = _base_get_text ( i , 0 , i , text [ i ] . length ( ) ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
clipboard + = " \n " ;
2022-06-03 16:47:37 +00:00
}
2021-07-08 15:48:58 +00:00
}
2022-06-03 16:47:37 +00:00
String clipboard_string = clipboard . as_string ( ) ;
DisplayServer : : get_singleton ( ) - > clipboard_set ( clipboard_string ) ;
2024-01-22 23:27:19 +00:00
// Set the cut copy line so we know to paste as a line.
if ( get_caret_count ( ) = = 1 ) {
cut_copy_line = clipboard_string ;
} else {
cut_copy_line = " " ;
}
2021-07-08 15:48:58 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : _paste_internal ( int p_caret ) {
2024-01-22 23:27:19 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2021-07-09 13:05:52 +00:00
if ( ! editable ) {
2021-07-08 15:48:58 +00:00
return ;
}
String clipboard = DisplayServer : : get_singleton ( ) - > clipboard_get ( ) ;
2024-01-22 23:27:19 +00:00
// Paste a full line. Ignore '\r' characters that may have been added to the clipboard by the OS.
if ( get_caret_count ( ) = = 1 & & ! has_selection ( 0 ) & & ! cut_copy_line . is_empty ( ) & & cut_copy_line = = clipboard . replace ( " \r " , " " ) ) {
insert_text ( clipboard , get_caret_line ( ) , 0 ) ;
return ;
}
// Paste text at each caret or one line per caret.
2022-06-03 16:47:37 +00:00
Vector < String > clipboad_lines = clipboard . split ( " \n " ) ;
2024-01-22 23:27:19 +00:00
bool insert_line_per_caret = p_caret = = - 1 & & get_caret_count ( ) > 1 & & clipboad_lines . size ( ) = = get_caret_count ( ) ;
2021-07-08 15:48:58 +00:00
begin_complex_operation ( ) ;
2024-01-22 23:27:19 +00:00
begin_multicaret_edit ( ) ;
Vector < int > sorted_carets = get_sorted_carets ( ) ;
2024-05-02 13:16:32 +00:00
for ( int i = 0 ; i < sorted_carets . size ( ) ; i + + ) {
2024-01-22 23:27:19 +00:00
int caret_index = sorted_carets [ i ] ;
if ( p_caret ! = - 1 & & p_caret ! = caret_index ) {
2022-06-03 16:47:37 +00:00
continue ;
}
2024-01-22 23:27:19 +00:00
if ( has_selection ( caret_index ) ) {
delete_selection ( caret_index ) ;
2022-06-03 16:47:37 +00:00
}
2021-07-08 15:48:58 +00:00
2022-06-03 16:47:37 +00:00
if ( insert_line_per_caret ) {
2024-01-22 23:27:19 +00:00
clipboard = clipboad_lines [ i ] ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
insert_text_at_caret ( clipboard , caret_index ) ;
2022-06-03 16:47:37 +00:00
}
2024-01-22 23:27:19 +00:00
end_multicaret_edit ( ) ;
2021-07-08 15:48:58 +00:00
end_complex_operation ( ) ;
}
2022-06-03 16:47:37 +00:00
void TextEdit : : _paste_primary_clipboard_internal ( int p_caret ) {
2024-01-22 23:27:19 +00:00
ERR_FAIL_COND ( p_caret > = get_caret_count ( ) | | p_caret < - 1 ) ;
2021-10-20 19:25:09 +00:00
if ( ! is_editable ( ) | | ! DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
2021-10-12 07:43:50 +00:00
return ;
}
String paste_buffer = DisplayServer : : get_singleton ( ) - > clipboard_get_primary ( ) ;
2024-01-22 23:27:19 +00:00
if ( get_caret_count ( ) = = 1 ) {
2022-06-03 16:47:37 +00:00
Point2i pos = get_line_column_at_pos ( get_local_mouse_pos ( ) ) ;
deselect ( ) ;
2024-01-22 23:27:19 +00:00
set_caret_line ( pos . y , true , false , - 1 ) ;
2022-06-03 16:47:37 +00:00
set_caret_column ( pos . x ) ;
}
2021-10-12 07:43:50 +00:00
if ( ! paste_buffer . is_empty ( ) ) {
insert_text_at_caret ( paste_buffer ) ;
}
grab_focus ( ) ;
}
2021-07-10 10:41:38 +00:00
// Context menu.
2021-08-13 21:31:57 +00:00
Key TextEdit : : _get_menu_action_accelerator ( const String & p_action ) {
2021-07-10 10:41:38 +00:00
const List < Ref < InputEvent > > * events = InputMap : : get_singleton ( ) - > action_get_events ( p_action ) ;
if ( ! events ) {
2021-08-13 21:31:57 +00:00
return Key : : NONE ;
2021-07-10 10:41:38 +00:00
}
// Use first event in the list for the accelerator.
const List < Ref < InputEvent > > : : Element * first_event = events - > front ( ) ;
if ( ! first_event ) {
2021-08-13 21:31:57 +00:00
return Key : : NONE ;
2021-07-10 10:41:38 +00:00
}
const Ref < InputEventKey > event = first_event - > get ( ) ;
if ( event . is_null ( ) ) {
2021-08-13 21:31:57 +00:00
return Key : : NONE ;
2021-07-10 10:41:38 +00:00
}
2023-03-08 22:00:27 +00:00
// Use physical keycode if non-zero.
2021-08-13 21:31:57 +00:00
if ( event - > get_physical_keycode ( ) ! = Key : : NONE ) {
2021-07-10 10:41:38 +00:00
return event - > get_physical_keycode_with_modifiers ( ) ;
} else {
return event - > get_keycode_with_modifiers ( ) ;
}
}
2023-01-26 17:59:41 +00:00
void TextEdit : : _generate_context_menu ( ) {
menu = memnew ( PopupMenu ) ;
add_child ( menu , false , INTERNAL_MODE_FRONT ) ;
menu_dir = memnew ( PopupMenu ) ;
2023-12-15 23:56:06 +00:00
menu_dir - > add_radio_check_item ( ETR ( " Same as Layout Direction " ) , MENU_DIR_INHERITED ) ;
menu_dir - > add_radio_check_item ( ETR ( " Auto-Detect Direction " ) , MENU_DIR_AUTO ) ;
menu_dir - > add_radio_check_item ( ETR ( " Left-to-Right " ) , MENU_DIR_LTR ) ;
menu_dir - > add_radio_check_item ( ETR ( " Right-to-Left " ) , MENU_DIR_RTL ) ;
2023-01-26 17:59:41 +00:00
menu_ctl = memnew ( PopupMenu ) ;
2023-12-15 23:56:06 +00:00
menu_ctl - > add_item ( ETR ( " Left-to-Right Mark (LRM) " ) , MENU_INSERT_LRM ) ;
menu_ctl - > add_item ( ETR ( " Right-to-Left Mark (RLM) " ) , MENU_INSERT_RLM ) ;
menu_ctl - > add_item ( ETR ( " Start of Left-to-Right Embedding (LRE) " ) , MENU_INSERT_LRE ) ;
menu_ctl - > add_item ( ETR ( " Start of Right-to-Left Embedding (RLE) " ) , MENU_INSERT_RLE ) ;
menu_ctl - > add_item ( ETR ( " Start of Left-to-Right Override (LRO) " ) , MENU_INSERT_LRO ) ;
menu_ctl - > add_item ( ETR ( " Start of Right-to-Left Override (RLO) " ) , MENU_INSERT_RLO ) ;
menu_ctl - > add_item ( ETR ( " Pop Direction Formatting (PDF) " ) , MENU_INSERT_PDF ) ;
2023-01-26 17:59:41 +00:00
menu_ctl - > add_separator ( ) ;
2023-12-15 23:56:06 +00:00
menu_ctl - > add_item ( ETR ( " Arabic Letter Mark (ALM) " ) , MENU_INSERT_ALM ) ;
menu_ctl - > add_item ( ETR ( " Left-to-Right Isolate (LRI) " ) , MENU_INSERT_LRI ) ;
menu_ctl - > add_item ( ETR ( " Right-to-Left Isolate (RLI) " ) , MENU_INSERT_RLI ) ;
menu_ctl - > add_item ( ETR ( " First Strong Isolate (FSI) " ) , MENU_INSERT_FSI ) ;
menu_ctl - > add_item ( ETR ( " Pop Direction Isolate (PDI) " ) , MENU_INSERT_PDI ) ;
2023-01-26 17:59:41 +00:00
menu_ctl - > add_separator ( ) ;
2023-12-15 23:56:06 +00:00
menu_ctl - > add_item ( ETR ( " Zero-Width Joiner (ZWJ) " ) , MENU_INSERT_ZWJ ) ;
menu_ctl - > add_item ( ETR ( " Zero-Width Non-Joiner (ZWNJ) " ) , MENU_INSERT_ZWNJ ) ;
menu_ctl - > add_item ( ETR ( " Word Joiner (WJ) " ) , MENU_INSERT_WJ ) ;
menu_ctl - > add_item ( ETR ( " Soft Hyphen (SHY) " ) , MENU_INSERT_SHY ) ;
menu - > add_item ( ETR ( " Cut " ) , MENU_CUT ) ;
menu - > add_item ( ETR ( " Copy " ) , MENU_COPY ) ;
menu - > add_item ( ETR ( " Paste " ) , MENU_PASTE ) ;
2023-01-26 17:59:41 +00:00
menu - > add_separator ( ) ;
2023-12-15 23:56:06 +00:00
menu - > add_item ( ETR ( " Select All " ) , MENU_SELECT_ALL ) ;
menu - > add_item ( ETR ( " Clear " ) , MENU_CLEAR ) ;
2023-01-26 17:59:41 +00:00
menu - > add_separator ( ) ;
2023-12-15 23:56:06 +00:00
menu - > add_item ( ETR ( " Undo " ) , MENU_UNDO ) ;
menu - > add_item ( ETR ( " Redo " ) , MENU_REDO ) ;
2023-01-26 17:59:41 +00:00
menu - > add_separator ( ) ;
2023-12-15 23:56:06 +00:00
menu - > add_submenu_node_item ( ETR ( " Text Writing Direction " ) , menu_dir , MENU_SUBMENU_TEXT_DIR ) ;
2023-01-26 17:59:41 +00:00
menu - > add_separator ( ) ;
2023-12-15 23:56:06 +00:00
menu - > add_check_item ( ETR ( " Display Control Characters " ) , MENU_DISPLAY_UCC ) ;
menu - > add_submenu_node_item ( ETR ( " Insert Control Character " ) , menu_ctl , MENU_SUBMENU_INSERT_UCC ) ;
2023-01-26 17:59:41 +00:00
2024-05-14 12:13:31 +00:00
menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & TextEdit : : menu_option ) ) ;
menu_dir - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & TextEdit : : menu_option ) ) ;
menu_ctl - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & TextEdit : : menu_option ) ) ;
2023-01-26 17:59:41 +00:00
}
void TextEdit : : _update_context_menu ( ) {
if ( ! menu ) {
_generate_context_menu ( ) ;
}
int idx = - 1 ;
# define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \
idx = m_menu - > get_item_index ( m_id ) ; \
if ( idx > = 0 ) { \
m_menu - > set_item_accelerator ( idx , shortcut_keys_enabled ? _get_menu_action_accelerator ( m_action ) : Key : : NONE ) ; \
m_menu - > set_item_disabled ( idx , m_disabled ) ; \
}
# define MENU_ITEM_ACTION(m_menu, m_id, m_action) \
idx = m_menu - > get_item_index ( m_id ) ; \
if ( idx > = 0 ) { \
m_menu - > set_item_accelerator ( idx , shortcut_keys_enabled ? _get_menu_action_accelerator ( m_action ) : Key : : NONE ) ; \
}
# define MENU_ITEM_DISABLED(m_menu, m_id, m_disabled) \
idx = m_menu - > get_item_index ( m_id ) ; \
if ( idx > = 0 ) { \
m_menu - > set_item_disabled ( idx , m_disabled ) ; \
}
# define MENU_ITEM_CHECKED(m_menu, m_id, m_checked) \
idx = m_menu - > get_item_index ( m_id ) ; \
if ( idx > = 0 ) { \
m_menu - > set_item_checked ( idx , m_checked ) ; \
}
MENU_ITEM_ACTION_DISABLED ( menu , MENU_CUT , " ui_cut " , ! editable )
MENU_ITEM_ACTION ( menu , MENU_COPY , " ui_copy " )
MENU_ITEM_ACTION_DISABLED ( menu , MENU_PASTE , " ui_paste " , ! editable )
MENU_ITEM_ACTION_DISABLED ( menu , MENU_SELECT_ALL , " ui_text_select_all " , ! selecting_enabled )
MENU_ITEM_DISABLED ( menu , MENU_CLEAR , ! editable )
MENU_ITEM_ACTION_DISABLED ( menu , MENU_UNDO , " ui_undo " , ! editable | | ! has_undo ( ) )
MENU_ITEM_ACTION_DISABLED ( menu , MENU_REDO , " ui_redo " , ! editable | | ! has_redo ( ) )
MENU_ITEM_CHECKED ( menu_dir , MENU_DIR_INHERITED , text_direction = = TEXT_DIRECTION_INHERITED )
MENU_ITEM_CHECKED ( menu_dir , MENU_DIR_AUTO , text_direction = = TEXT_DIRECTION_AUTO )
MENU_ITEM_CHECKED ( menu_dir , MENU_DIR_LTR , text_direction = = TEXT_DIRECTION_LTR )
MENU_ITEM_CHECKED ( menu_dir , MENU_DIR_RTL , text_direction = = TEXT_DIRECTION_RTL )
MENU_ITEM_CHECKED ( menu , MENU_DISPLAY_UCC , draw_control_chars )
MENU_ITEM_DISABLED ( menu , MENU_SUBMENU_INSERT_UCC , ! editable )
# undef MENU_ITEM_ACTION_DISABLED
# undef MENU_ITEM_ACTION
# undef MENU_ITEM_DISABLED
# undef MENU_ITEM_CHECKED
}
2021-07-10 10:41:38 +00:00
/* Versioning */
void TextEdit : : _push_current_op ( ) {
2022-06-03 16:47:37 +00:00
if ( pending_action_end ) {
start_action ( EditAction : : ACTION_NONE ) ;
return ;
}
2021-07-10 10:41:38 +00:00
if ( current_op . type = = TextOperation : : TYPE_NONE ) {
return ; // Nothing to do.
}
if ( next_operation_is_complex ) {
current_op . chain_forward = true ;
next_operation_is_complex = false ;
}
undo_stack . push_back ( current_op ) ;
current_op . type = TextOperation : : TYPE_NONE ;
current_op . text = " " ;
current_op . chain_forward = false ;
if ( undo_stack . size ( ) > undo_stack_max_size ) {
undo_stack . pop_front ( ) ;
}
}
void TextEdit : : _do_text_op ( const TextOperation & p_op , bool p_reverse ) {
ERR_FAIL_COND ( p_op . type = = TextOperation : : TYPE_NONE ) ;
bool insert = p_op . type = = TextOperation : : TYPE_INSERT ;
if ( p_reverse ) {
insert = ! insert ;
}
if ( insert ) {
int check_line ;
int check_column ;
_base_insert_text ( p_op . from_line , p_op . from_column , p_op . text , check_line , check_column ) ;
ERR_FAIL_COND ( check_line ! = p_op . to_line ) ; // BUG.
ERR_FAIL_COND ( check_column ! = p_op . to_column ) ; // BUG.
} else {
_base_remove_text ( p_op . from_line , p_op . from_column , p_op . to_line , p_op . to_column ) ;
}
}
void TextEdit : : _clear_redo ( ) {
if ( undo_stack_pos = = nullptr ) {
return ; // Nothing to clear.
}
_push_current_op ( ) ;
while ( undo_stack_pos ) {
List < TextOperation > : : Element * elem = undo_stack_pos ;
undo_stack_pos = undo_stack_pos - > next ( ) ;
undo_stack . erase ( elem ) ;
}
}
/* Search */
int TextEdit : : _get_column_pos_of_word ( const String & p_key , const String & p_search , uint32_t p_search_flags , int p_from_column ) const {
int col = - 1 ;
if ( p_key . length ( ) > 0 & & p_search . length ( ) > 0 ) {
if ( p_from_column < 0 | | p_from_column > p_search . length ( ) ) {
p_from_column = 0 ;
}
2023-10-02 20:30:23 +00:00
bool key_start_is_symbol = is_symbol ( p_key [ 0 ] ) ;
bool key_end_is_symbol = is_symbol ( p_key [ p_key . length ( ) - 1 ] ) ;
2021-07-10 10:41:38 +00:00
while ( col = = - 1 & & p_from_column < = p_search . length ( ) ) {
if ( p_search_flags & SEARCH_MATCH_CASE ) {
col = p_search . find ( p_key , p_from_column ) ;
} else {
col = p_search . findn ( p_key , p_from_column ) ;
}
2023-08-20 08:49:16 +00:00
// If not found, just break early to improve performance.
if ( col = = - 1 ) {
break ;
}
2021-07-10 10:41:38 +00:00
// Whole words only.
if ( col ! = - 1 & & p_search_flags & SEARCH_WHOLE_WORDS ) {
p_from_column = col ;
2023-10-02 20:30:23 +00:00
if ( ! key_start_is_symbol & & col > 0 & & ! is_symbol ( p_search [ col - 1 ] ) ) {
2021-07-10 10:41:38 +00:00
col = - 1 ;
2023-10-02 20:30:23 +00:00
} else if ( ! key_end_is_symbol & & ( col + p_key . length ( ) ) < p_search . length ( ) & & ! is_symbol ( p_search [ col + p_key . length ( ) ] ) ) {
2021-07-10 10:41:38 +00:00
col = - 1 ;
}
}
p_from_column + = 1 ;
}
}
return col ;
}
/* Mouse */
int TextEdit : : _get_char_pos_for_line ( int p_px , int p_line , int p_wrap_index ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
p_wrap_index = MIN ( p_wrap_index , text . get_line_data ( p_line ) - > get_line_count ( ) - 1 ) ;
RID text_rid = text . get_line_data ( p_line ) - > get_line_rid ( p_wrap_index ) ;
2024-02-19 11:41:12 +00:00
float wrap_indent = ( text . is_indent_wrapped_lines ( ) & & p_wrap_index > 0 ) ? get_indent_level ( p_line ) * theme_cache . font - > get_char_size ( ' ' , theme_cache . font_size ) . width : 0.0 ;
2021-07-10 10:41:38 +00:00
if ( is_layout_rtl ( ) ) {
2024-02-19 11:41:12 +00:00
p_px = TS - > shaped_text_get_size ( text_rid ) . x - p_px + wrap_indent ;
} else {
p_px - = wrap_indent ;
2021-07-10 10:41:38 +00:00
}
2023-08-15 08:42:40 +00:00
int ofs = TS - > shaped_text_hit_test_position ( text_rid , p_px ) ;
if ( ! caret_mid_grapheme_enabled ) {
ofs = TS - > shaped_text_closest_character_pos ( text_rid , ofs ) ;
}
return ofs ;
2021-07-10 10:41:38 +00:00
}
2021-07-08 17:35:56 +00:00
/* Caret */
2024-01-22 23:26:16 +00:00
void TextEdit : : _caret_changed ( int p_caret ) {
queue_redraw ( ) ;
if ( has_selection ( p_caret ) ) {
_selection_changed ( p_caret ) ;
}
if ( caret_pos_dirty ) {
return ;
}
if ( is_inside_tree ( ) ) {
callable_mp ( this , & TextEdit : : _emit_caret_changed ) . call_deferred ( ) ;
}
caret_pos_dirty = true ;
}
2021-07-08 17:35:56 +00:00
void TextEdit : : _emit_caret_changed ( ) {
emit_signal ( SNAME ( " caret_changed " ) ) ;
caret_pos_dirty = false ;
}
void TextEdit : : _reset_caret_blink_timer ( ) {
if ( ! caret_blink_enabled ) {
return ;
}
draw_caret = true ;
if ( has_focus ( ) ) {
caret_blink_timer - > stop ( ) ;
caret_blink_timer - > start ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-08 17:35:56 +00:00
}
}
void TextEdit : : _toggle_draw_caret ( ) {
draw_caret = ! draw_caret ;
if ( is_visible_in_tree ( ) & & has_focus ( ) & & window_has_focus ) {
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-08 17:35:56 +00:00
}
}
2022-06-03 16:47:37 +00:00
int TextEdit : : _get_column_x_offset_for_line ( int p_char , int p_line , int p_column ) const {
2021-07-10 10:41:38 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
int row = 0 ;
Vector < Vector2i > rows2 = text . get_line_wrap_ranges ( p_line ) ;
for ( int i = 0 ; i < rows2 . size ( ) ; i + + ) {
2022-04-22 16:52:31 +00:00
if ( ( p_char > = rows2 [ i ] . x ) & & ( p_char < = rows2 [ i ] . y ) ) {
2021-07-10 10:41:38 +00:00
row = i ;
break ;
}
}
RID text_rid = text . get_line_data ( p_line ) - > get_line_rid ( row ) ;
2024-02-19 11:41:12 +00:00
float wrap_indent = ( text . is_indent_wrapped_lines ( ) & & row > 0 ) ? get_indent_level ( p_line ) * theme_cache . font - > get_char_size ( ' ' , theme_cache . font_size ) . width : 0.0 ;
2022-06-03 16:47:37 +00:00
CaretInfo ts_caret = TS - > shaped_text_get_carets ( text_rid , p_column ) ;
2021-08-27 21:19:51 +00:00
if ( ( ts_caret . l_caret ! = Rect2 ( ) & & ( ts_caret . l_dir = = TextServer : : DIRECTION_AUTO | | ts_caret . l_dir = = ( TextServer : : Direction ) input_direction ) ) | | ( ts_caret . t_caret = = Rect2 ( ) ) ) {
2024-02-19 11:41:12 +00:00
return ts_caret . l_caret . position . x + ( is_layout_rtl ( ) ? - wrap_indent : wrap_indent ) ;
2021-07-10 10:41:38 +00:00
} else {
2024-02-19 11:41:12 +00:00
return ts_caret . t_caret . position . x + ( is_layout_rtl ( ) ? - wrap_indent : wrap_indent ) ;
2021-07-10 10:41:38 +00:00
}
}
2024-01-22 23:26:16 +00:00
bool TextEdit : : _is_line_col_in_range ( int p_line , int p_column , int p_from_line , int p_from_column , int p_to_line , int p_to_column , bool p_include_edges ) const {
if ( p_line > = p_from_line & & p_line < = p_to_line & & ( p_line > p_from_line | | p_column > p_from_column ) & & ( p_line < p_to_line | | p_column < p_to_column ) ) {
return true ;
}
if ( p_include_edges ) {
if ( ( p_line = = p_from_line & & p_column = = p_from_column ) | | ( p_line = = p_to_line & & p_column = = p_to_column ) ) {
return true ;
}
}
return false ;
}
2024-01-22 23:27:19 +00:00
void TextEdit : : _offset_carets_after ( int p_old_line , int p_old_column , int p_new_line , int p_new_column , bool p_include_selection_begin , bool p_include_selection_end ) {
// Moves all carets at or after old_line and old_column.
// Called after deleting or inserting text so that the carets stay with the text they are at.
int edit_height = p_new_line - p_old_line ;
int edit_size = p_new_column - p_old_column ;
if ( edit_height = = 0 & & edit_size = = 0 ) {
return ;
}
// Intentionally includes carets in the multicaret_edit_ignore list so that they are moved together.
for ( int i = 0 ; i < get_caret_count ( ) ; i + + ) {
bool selected = has_selection ( i ) ;
bool caret_at_end = selected & & is_caret_after_selection_origin ( i ) ;
bool include_caret_at = caret_at_end ? p_include_selection_end : p_include_selection_begin ;
// Move caret.
int caret_line = get_caret_line ( i ) ;
int caret_column = get_caret_column ( i ) ;
bool caret_after = caret_line > p_old_line | | ( caret_line = = p_old_line & & caret_column > p_old_column ) ;
bool caret_at = caret_line = = p_old_line & & caret_column = = p_old_column ;
if ( caret_after | | ( caret_at & & include_caret_at ) ) {
caret_line + = edit_height ;
if ( caret_line = = p_new_line ) {
caret_column + = edit_size ;
}
if ( edit_height ! = 0 ) {
set_caret_line ( caret_line , false , true , - 1 , i ) ;
}
set_caret_column ( caret_column , false , i ) ;
}
// Move selection origin.
if ( ! selected ) {
continue ;
}
bool include_selection_origin_at = ! caret_at_end ? p_include_selection_end : p_include_selection_begin ;
int selection_origin_line = get_selection_origin_line ( i ) ;
int selection_origin_column = get_selection_origin_column ( i ) ;
bool selection_origin_after = selection_origin_line > p_old_line | | ( selection_origin_line = = p_old_line & & selection_origin_column > p_old_column ) ;
bool selection_origin_at = selection_origin_line = = p_old_line & & selection_origin_column = = p_old_column ;
if ( selection_origin_after | | ( selection_origin_at & & include_selection_origin_at ) ) {
selection_origin_line + = edit_height ;
if ( selection_origin_line = = p_new_line ) {
selection_origin_column + = edit_size ;
}
select ( selection_origin_line , selection_origin_column , caret_line , caret_column , i ) ;
}
}
if ( ! p_include_selection_begin & & p_include_selection_end & & has_selection ( ) ) {
// It is possible that two adjacent selections now overlap.
merge_overlapping_carets ( ) ;
}
}
2024-01-22 23:26:16 +00:00
void TextEdit : : _cancel_drag_and_drop_text ( ) {
// Cancel the drag operation if drag originated from here.
if ( selection_drag_attempt & & get_viewport ( ) ) {
get_viewport ( ) - > gui_cancel_drag ( ) ;
}
}
2021-07-09 11:42:55 +00:00
/* Selection */
2024-01-22 23:26:16 +00:00
void TextEdit : : _selection_changed ( int p_caret ) {
if ( ! selecting_enabled ) {
return ;
}
_cancel_drag_and_drop_text ( ) ;
queue_redraw ( ) ;
}
2021-07-09 11:42:55 +00:00
void TextEdit : : _click_selection_held ( ) {
2024-01-22 23:26:16 +00:00
// Update the selection mode on a timer so it is updated when the view scrolls even if the mouse isn't moving.
if ( ! Input : : get_singleton ( ) - > is_mouse_button_pressed ( MouseButton : : LEFT ) | | get_selection_mode ( ) = = SelectionMode : : SELECTION_MODE_NONE ) {
2021-07-09 11:42:55 +00:00
click_select_held - > stop ( ) ;
2024-01-22 23:26:16 +00:00
return ;
}
switch ( get_selection_mode ( ) ) {
case SelectionMode : : SELECTION_MODE_POINTER : {
_update_selection_mode_pointer ( ) ;
} break ;
case SelectionMode : : SELECTION_MODE_WORD : {
_update_selection_mode_word ( ) ;
} break ;
case SelectionMode : : SELECTION_MODE_LINE : {
_update_selection_mode_line ( ) ;
} break ;
default : {
break ;
}
2021-07-09 11:42:55 +00:00
}
}
2024-01-22 23:26:16 +00:00
void TextEdit : : _update_selection_mode_pointer ( bool p_initial ) {
2021-07-10 10:41:38 +00:00
Point2 mp = get_local_mouse_pos ( ) ;
2021-07-09 11:42:55 +00:00
2021-09-23 14:58:43 +00:00
Point2i pos = get_line_column_at_pos ( mp ) ;
2021-07-10 10:41:38 +00:00
int line = pos . y ;
2024-01-22 23:26:16 +00:00
int column = pos . x ;
int caret_index = get_caret_count ( ) - 1 ;
if ( p_initial & & ! has_selection ( caret_index ) ) {
set_selection_origin_line ( line , true , - 1 , caret_index ) ;
set_selection_origin_column ( column , caret_index ) ;
// Set the word begin and end to the column in case the mode changes later.
carets . write [ caret_index ] . selection . word_begin_column = column ;
carets . write [ caret_index ] . selection . word_end_column = column ;
} else {
select ( get_selection_origin_line ( caret_index ) , get_selection_origin_column ( caret_index ) , line , column , caret_index ) ;
}
adjust_viewport_to_caret ( caret_index ) ;
2021-07-09 11:42:55 +00:00
2024-01-22 23:26:16 +00:00
if ( has_selection ( caret_index ) ) {
// Only set to true if any selection has been made.
dragging_selection = true ;
}
2021-07-09 11:42:55 +00:00
click_select_held - > start ( ) ;
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-09 11:42:55 +00:00
}
2024-01-22 23:26:16 +00:00
void TextEdit : : _update_selection_mode_word ( bool p_initial ) {
2021-07-09 11:42:55 +00:00
dragging_selection = true ;
2021-07-10 10:41:38 +00:00
Point2 mp = get_local_mouse_pos ( ) ;
2021-07-09 11:42:55 +00:00
2021-09-23 14:58:43 +00:00
Point2i pos = get_line_column_at_pos ( mp ) ;
2021-07-10 10:41:38 +00:00
int line = pos . y ;
2024-01-22 23:26:16 +00:00
int column = pos . x ;
int caret_index = get_caret_count ( ) - 1 ;
2021-07-09 11:42:55 +00:00
2024-01-22 23:26:16 +00:00
int caret_pos = CLAMP ( column , 0 , text [ line ] . length ( ) ) ;
2021-07-09 11:42:55 +00:00
int beg = caret_pos ;
int end = beg ;
2021-08-27 21:19:51 +00:00
PackedInt32Array words = TS - > shaped_text_get_word_breaks ( text . get_line_data ( line ) - > get_rid ( ) ) ;
for ( int i = 0 ; i < words . size ( ) ; i = i + 2 ) {
if ( ( words [ i ] < caret_pos & & words [ i + 1 ] > caret_pos ) | | ( i = = words . size ( ) - 2 & & caret_pos = = words [ i + 1 ] ) ) {
beg = words [ i ] ;
end = words [ i + 1 ] ;
2021-07-09 11:42:55 +00:00
break ;
}
}
2024-01-22 23:26:16 +00:00
if ( p_initial & & ! has_selection ( caret_index ) ) {
// Set the selection origin if there is no existing selection.
select ( line , beg , line , end , caret_index ) ;
carets . write [ caret_index ] . selection . word_begin_column = beg ;
carets . write [ caret_index ] . selection . word_end_column = end ;
2021-07-09 11:42:55 +00:00
} else {
2024-01-22 23:26:16 +00:00
// Expand the word selection to the mouse.
int origin_line = get_selection_origin_line ( caret_index ) ;
bool is_new_selection_dir_right = line > origin_line | | ( line = = origin_line & & column > = carets [ caret_index ] . selection . word_begin_column ) ;
int origin_col = is_new_selection_dir_right ? carets [ caret_index ] . selection . word_begin_column : carets [ caret_index ] . selection . word_end_column ;
int caret_col = is_new_selection_dir_right ? end : beg ;
select ( origin_line , origin_col , line , caret_col , caret_index ) ;
2021-07-09 11:42:55 +00:00
}
2024-01-22 23:26:16 +00:00
adjust_viewport_to_caret ( caret_index ) ;
2021-07-09 11:42:55 +00:00
2021-10-20 19:25:09 +00:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
DisplayServer : : get_singleton ( ) - > clipboard_set_primary ( get_selected_text ( ) ) ;
}
2021-10-12 07:43:50 +00:00
2021-07-09 11:42:55 +00:00
click_select_held - > start ( ) ;
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-09 11:42:55 +00:00
}
2024-01-22 23:26:16 +00:00
void TextEdit : : _update_selection_mode_line ( bool p_initial ) {
2021-07-09 11:42:55 +00:00
dragging_selection = true ;
2021-07-10 10:41:38 +00:00
Point2 mp = get_local_mouse_pos ( ) ;
2021-07-09 11:42:55 +00:00
2021-09-23 14:58:43 +00:00
Point2i pos = get_line_column_at_pos ( mp ) ;
2021-07-10 10:41:38 +00:00
int line = pos . y ;
2024-01-22 23:26:16 +00:00
int caret_index = get_caret_count ( ) - 1 ;
int origin_line = p_initial & & ! has_selection ( caret_index ) ? line : get_selection_origin_line ( ) ;
bool line_below = line > = origin_line ;
int origin_col = line_below ? 0 : get_line ( origin_line ) . length ( ) ;
int caret_line = line_below ? line + 1 : line ;
int caret_col = caret_line < text . size ( ) ? 0 : get_line ( text . size ( ) - 1 ) . length ( ) ;
select ( origin_line , origin_col , caret_line , caret_col , caret_index ) ;
adjust_viewport_to_caret ( caret_index ) ;
if ( p_initial ) {
// Set the word begin and end to the start and end of the origin line in case the mode changes later.
carets . write [ caret_index ] . selection . word_begin_column = 0 ;
carets . write [ caret_index ] . selection . word_end_column = get_line ( origin_line ) . length ( ) ;
2021-07-09 11:42:55 +00:00
}
2021-10-20 19:25:09 +00:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
DisplayServer : : get_singleton ( ) - > clipboard_set_primary ( get_selected_text ( ) ) ;
}
2021-10-12 07:43:50 +00:00
2021-07-09 11:42:55 +00:00
click_select_held - > start ( ) ;
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-09 11:42:55 +00:00
}
2022-06-03 16:47:37 +00:00
void TextEdit : : _pre_shift_selection ( int p_caret ) {
2022-04-22 16:52:31 +00:00
if ( ! selecting_enabled ) {
return ;
}
2024-01-22 23:26:16 +00:00
set_selection_mode ( SelectionMode : : SELECTION_MODE_SHIFT ) ;
if ( has_selection ( p_caret ) ) {
2022-06-03 16:47:37 +00:00
return ;
2021-07-09 11:42:55 +00:00
}
2024-01-22 23:26:16 +00:00
// Prepare selection to start at current caret position.
set_selection_origin_line ( get_caret_line ( p_caret ) , true , - 1 , p_caret ) ;
set_selection_origin_column ( get_caret_column ( p_caret ) , p_caret ) ;
carets . write [ p_caret ] . selection . active = true ;
carets . write [ p_caret ] . selection . word_begin_column = get_caret_column ( p_caret ) ;
carets . write [ p_caret ] . selection . word_end_column = get_caret_column ( p_caret ) ;
2021-07-09 11:42:55 +00:00
}
2024-01-22 23:26:16 +00:00
bool TextEdit : : _selection_contains ( int p_caret , int p_line , int p_column , bool p_include_edges , bool p_only_selections ) const {
if ( ! has_selection ( p_caret ) ) {
return ! p_only_selections & & p_line = = get_caret_line ( p_caret ) & & p_column = = get_caret_column ( p_caret ) ;
2021-07-09 11:42:55 +00:00
}
2024-01-22 23:26:16 +00:00
return _is_line_col_in_range ( p_line , p_column , get_selection_from_line ( p_caret ) , get_selection_from_column ( p_caret ) , get_selection_to_line ( p_caret ) , get_selection_to_column ( p_caret ) , p_include_edges ) ;
2021-07-09 11:42:55 +00:00
}
2021-07-09 10:52:49 +00:00
/* Line Wrapping */
void TextEdit : : _update_wrap_at_column ( bool p_force ) {
2023-04-03 16:01:10 +00:00
int new_wrap_at = get_size ( ) . width - theme_cache . style_normal - > get_minimum_size ( ) . width - gutters_width - gutter_padding ;
2021-07-09 10:52:49 +00:00
if ( draw_minimap ) {
new_wrap_at - = minimap_width ;
}
if ( v_scroll - > is_visible_in_tree ( ) ) {
new_wrap_at - = v_scroll - > get_combined_minimum_size ( ) . width ;
}
/* Give it a little more space. */
new_wrap_at - = wrap_right_offset ;
if ( ( wrap_at_column ! = new_wrap_at ) | | p_force ) {
wrap_at_column = new_wrap_at ;
if ( line_wrapping_mode ) {
2023-03-12 11:21:00 +00:00
BitField < TextServer : : LineBreakFlag > autowrap_flags = TextServer : : BREAK_MANDATORY ;
switch ( autowrap_mode ) {
case TextServer : : AUTOWRAP_WORD_SMART :
autowrap_flags = TextServer : : BREAK_WORD_BOUND | TextServer : : BREAK_ADAPTIVE | TextServer : : BREAK_MANDATORY ;
break ;
case TextServer : : AUTOWRAP_WORD :
autowrap_flags = TextServer : : BREAK_WORD_BOUND | TextServer : : BREAK_MANDATORY ;
break ;
case TextServer : : AUTOWRAP_ARBITRARY :
autowrap_flags = TextServer : : BREAK_GRAPHEME_BOUND | TextServer : : BREAK_MANDATORY ;
break ;
case TextServer : : AUTOWRAP_OFF :
break ;
}
text . set_brk_flags ( autowrap_flags ) ;
2021-07-09 10:52:49 +00:00
text . set_width ( wrap_at_column ) ;
} else {
text . set_width ( - 1 ) ;
}
2023-03-12 11:21:00 +00:00
2021-07-09 10:52:49 +00:00
text . invalidate_all_lines ( ) ;
2022-01-23 20:07:35 +00:00
_update_placeholder ( ) ;
2021-07-09 10:52:49 +00:00
}
2022-04-29 15:23:32 +00:00
// Update viewport.
2021-07-09 10:52:49 +00:00
int first_vis_line = get_first_visible_line ( ) ;
if ( is_line_wrapped ( first_vis_line ) ) {
2022-04-29 15:23:32 +00:00
first_visible_line_wrap_ofs = MIN ( first_visible_line_wrap_ofs , get_line_wrap_count ( first_vis_line ) ) ;
2021-07-09 10:52:49 +00:00
} else {
2022-04-29 15:23:32 +00:00
first_visible_line_wrap_ofs = 0 ;
2021-07-09 10:52:49 +00:00
}
2022-04-29 15:23:32 +00:00
set_line_as_first_visible ( first_visible_line , first_visible_line_wrap_ofs ) ;
2021-07-09 10:52:49 +00:00
}
2021-07-09 16:27:09 +00:00
/* Viewport. */
void TextEdit : : _update_scrollbars ( ) {
Size2 size = get_size ( ) ;
Size2 hmin = h_scroll - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scroll - > get_combined_minimum_size ( ) ;
2023-04-03 16:01:10 +00:00
v_scroll - > set_begin ( Point2 ( size . width - vmin . width , theme_cache . style_normal - > get_margin ( SIDE_TOP ) ) ) ;
v_scroll - > set_end ( Point2 ( size . width , size . height - theme_cache . style_normal - > get_margin ( SIDE_TOP ) - theme_cache . style_normal - > get_margin ( SIDE_BOTTOM ) ) ) ;
2021-07-09 16:27:09 +00:00
h_scroll - > set_begin ( Point2 ( 0 , size . height - hmin . height ) ) ;
h_scroll - > set_end ( Point2 ( size . width - vmin . width , size . height ) ) ;
2024-07-27 21:14:51 +00:00
bool draw_placeholder = _using_placeholder ( ) ;
2022-01-23 20:07:35 +00:00
2021-07-09 16:27:09 +00:00
int visible_rows = get_visible_line_count ( ) ;
2024-07-27 21:14:51 +00:00
int total_rows = draw_placeholder ? placeholder_wraped_rows . size ( ) : get_total_visible_line_count ( ) ;
2024-02-02 21:45:06 +00:00
if ( scroll_past_end_of_file_enabled & & ! fit_content_height ) {
2021-07-09 16:27:09 +00:00
total_rows + = visible_rows - 1 ;
}
2023-04-03 16:01:10 +00:00
int visible_width = size . width - theme_cache . style_normal - > get_minimum_size ( ) . width ;
2023-10-13 17:49:58 +00:00
int total_width = ( draw_placeholder ? placeholder_max_width : text . get_max_width ( ) ) + gutters_width + gutter_padding ;
2021-07-09 16:27:09 +00:00
if ( draw_minimap ) {
total_width + = minimap_width ;
}
2022-06-20 23:19:54 +00:00
content_height_cache = MAX ( total_rows , 1 ) * get_line_height ( ) ;
if ( fit_content_height ) {
update_minimum_size ( ) ;
}
2021-07-09 16:27:09 +00:00
updating_scrolls = true ;
2024-04-25 23:19:08 +00:00
if ( ! fit_content_height & & total_rows > visible_rows ) {
2021-07-09 16:27:09 +00:00
v_scroll - > show ( ) ;
v_scroll - > set_max ( total_rows + _get_visible_lines_offset ( ) ) ;
v_scroll - > set_page ( visible_rows + _get_visible_lines_offset ( ) ) ;
set_v_scroll ( get_v_scroll ( ) ) ;
} else {
2022-04-29 15:23:32 +00:00
first_visible_line = 0 ;
first_visible_line_wrap_ofs = 0 ;
2021-07-09 16:27:09 +00:00
v_scroll - > set_value ( 0 ) ;
2022-01-09 00:45:07 +00:00
v_scroll - > set_max ( 0 ) ;
2021-07-09 16:27:09 +00:00
v_scroll - > hide ( ) ;
}
2023-03-12 11:21:00 +00:00
if ( total_width > visible_width ) {
2021-07-09 16:27:09 +00:00
h_scroll - > show ( ) ;
h_scroll - > set_max ( total_width ) ;
h_scroll - > set_page ( visible_width ) ;
2022-04-29 15:23:32 +00:00
if ( first_visible_col > ( total_width - visible_width ) ) {
first_visible_col = ( total_width - visible_width ) ;
2021-07-09 16:27:09 +00:00
}
2022-04-29 15:23:32 +00:00
if ( fabs ( h_scroll - > get_value ( ) - ( double ) first_visible_col ) > = 1 ) {
h_scroll - > set_value ( first_visible_col ) ;
2021-07-09 16:27:09 +00:00
}
} else {
2022-04-29 15:23:32 +00:00
first_visible_col = 0 ;
2021-07-09 16:27:09 +00:00
h_scroll - > set_value ( 0 ) ;
2022-01-09 00:45:07 +00:00
h_scroll - > set_max ( 0 ) ;
2021-07-09 16:27:09 +00:00
h_scroll - > hide ( ) ;
}
2021-07-10 10:41:38 +00:00
updating_scrolls = false ;
}
int TextEdit : : _get_control_height ( ) const {
int control_height = get_size ( ) . height ;
2023-04-03 16:01:10 +00:00
control_height - = theme_cache . style_normal - > get_minimum_size ( ) . height ;
2021-07-10 10:41:38 +00:00
if ( h_scroll - > is_visible_in_tree ( ) ) {
control_height - = h_scroll - > get_size ( ) . height ;
}
return control_height ;
2021-07-09 16:27:09 +00:00
}
void TextEdit : : _v_scroll_input ( ) {
scrolling = false ;
minimap_clicked = false ;
}
void TextEdit : : _scroll_moved ( double p_to_val ) {
if ( updating_scrolls ) {
return ;
}
if ( h_scroll - > is_visible_in_tree ( ) ) {
2022-04-29 15:23:32 +00:00
first_visible_col = h_scroll - > get_value ( ) ;
2021-07-09 16:27:09 +00:00
}
if ( v_scroll - > is_visible_in_tree ( ) ) {
// Set line ofs and wrap ofs.
2024-07-27 21:14:51 +00:00
bool draw_placeholder = _using_placeholder ( ) ;
2022-01-23 20:07:35 +00:00
2021-07-09 16:27:09 +00:00
int v_scroll_i = floor ( get_v_scroll ( ) ) ;
int sc = 0 ;
int n_line ;
for ( n_line = 0 ; n_line < text . size ( ) ; n_line + + ) {
if ( ! _is_line_hidden ( n_line ) ) {
sc + + ;
2022-01-23 20:07:35 +00:00
sc + = draw_placeholder ? placeholder_wraped_rows . size ( ) - 1 : get_line_wrap_count ( n_line ) ;
2021-07-09 16:27:09 +00:00
if ( sc > v_scroll_i ) {
break ;
}
}
}
n_line = MIN ( n_line , text . size ( ) - 1 ) ;
2022-01-25 17:41:05 +00:00
int line_wrap_amount = draw_placeholder ? placeholder_wraped_rows . size ( ) - 1 : get_line_wrap_count ( n_line ) ;
2021-07-09 16:27:09 +00:00
int wi = line_wrap_amount - ( sc - v_scroll_i - 1 ) ;
wi = CLAMP ( wi , 0 , line_wrap_amount ) ;
2022-04-29 15:23:32 +00:00
first_visible_line = n_line ;
first_visible_line_wrap_ofs = wi ;
2021-07-09 16:27:09 +00:00
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-09 16:27:09 +00:00
}
double TextEdit : : _get_visible_lines_offset ( ) const {
double total = _get_control_height ( ) ;
2021-07-10 10:41:38 +00:00
total / = ( double ) get_line_height ( ) ;
2021-07-09 16:27:09 +00:00
total = total - floor ( total ) ;
total = - CLAMP ( total , 0.001 , 1 ) + 1 ;
return total ;
}
double TextEdit : : _get_v_scroll_offset ( ) const {
double val = get_v_scroll ( ) - floor ( get_v_scroll ( ) ) ;
return CLAMP ( val , 0 , 1 ) ;
}
2023-02-17 13:50:46 +00:00
void TextEdit : : _scroll_up ( real_t p_delta , bool p_animate ) {
2021-10-15 23:22:57 +00:00
if ( scrolling & & smooth_scroll_enabled & & SIGN ( target_v_scroll - v_scroll - > get_value ( ) ) ! = SIGN ( - p_delta ) ) {
2021-07-09 16:27:09 +00:00
scrolling = false ;
minimap_clicked = false ;
}
if ( scrolling ) {
target_v_scroll = ( target_v_scroll - p_delta ) ;
} else {
target_v_scroll = ( get_v_scroll ( ) - p_delta ) ;
}
if ( smooth_scroll_enabled ) {
if ( target_v_scroll < = 0 ) {
target_v_scroll = 0 ;
}
2023-10-12 10:27:35 +00:00
if ( ! p_animate | | Math : : abs ( target_v_scroll - v_scroll - > get_value ( ) ) < 1.0 ) {
2021-07-09 16:27:09 +00:00
v_scroll - > set_value ( target_v_scroll ) ;
} else {
scrolling = true ;
set_physics_process_internal ( true ) ;
}
} else {
set_v_scroll ( target_v_scroll ) ;
}
}
2023-02-17 13:50:46 +00:00
void TextEdit : : _scroll_down ( real_t p_delta , bool p_animate ) {
2021-10-15 23:22:57 +00:00
if ( scrolling & & smooth_scroll_enabled & & SIGN ( target_v_scroll - v_scroll - > get_value ( ) ) ! = SIGN ( p_delta ) ) {
2021-07-09 16:27:09 +00:00
scrolling = false ;
minimap_clicked = false ;
}
if ( scrolling ) {
target_v_scroll = ( target_v_scroll + p_delta ) ;
} else {
target_v_scroll = ( get_v_scroll ( ) + p_delta ) ;
}
if ( smooth_scroll_enabled ) {
int max_v_scroll = round ( v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ) ;
if ( target_v_scroll > max_v_scroll ) {
target_v_scroll = max_v_scroll ;
}
2023-10-12 10:27:35 +00:00
if ( ! p_animate | | Math : : abs ( target_v_scroll - v_scroll - > get_value ( ) ) < 1.0 ) {
2021-07-09 16:27:09 +00:00
v_scroll - > set_value ( target_v_scroll ) ;
} else {
scrolling = true ;
set_physics_process_internal ( true ) ;
}
} else {
set_v_scroll ( target_v_scroll ) ;
}
}
void TextEdit : : _scroll_lines_up ( ) {
scrolling = false ;
minimap_clicked = false ;
// Adjust the vertical scroll.
set_v_scroll ( get_v_scroll ( ) - 1 ) ;
// Adjust the caret to viewport.
2022-06-03 16:47:37 +00:00
for ( int i = 0 ; i < carets . size ( ) ; i + + ) {
if ( has_selection ( i ) ) {
continue ;
}
2021-07-09 16:27:09 +00:00
int last_vis_line = get_last_full_visible_line ( ) ;
int last_vis_wrap = get_last_full_visible_line_wrap_index ( ) ;
2022-06-03 16:47:37 +00:00
if ( get_caret_line ( i ) > last_vis_line | | ( get_caret_line ( i ) = = last_vis_line & & get_caret_wrap_index ( i ) > last_vis_wrap ) ) {
set_caret_line ( last_vis_line , false , false , last_vis_wrap , i ) ;
2021-07-09 16:27:09 +00:00
}
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-09 16:27:09 +00:00
}
void TextEdit : : _scroll_lines_down ( ) {
scrolling = false ;
minimap_clicked = false ;
// Adjust the vertical scroll.
set_v_scroll ( get_v_scroll ( ) + 1 ) ;
// Adjust the caret to viewport.
2022-06-03 16:47:37 +00:00
for ( int i = 0 ; i < carets . size ( ) ; i + + ) {
if ( has_selection ( i ) ) {
continue ;
}
2021-07-09 16:27:09 +00:00
2022-06-03 16:47:37 +00:00
int first_vis_line = get_first_visible_line ( ) ;
if ( get_caret_line ( i ) < first_vis_line | | ( get_caret_line ( i ) = = first_vis_line & & get_caret_wrap_index ( i ) < first_visible_line_wrap_ofs ) ) {
set_caret_line ( first_vis_line , false , false , first_visible_line_wrap_ofs , i ) ;
2021-07-09 16:27:09 +00:00
}
}
2022-06-03 16:47:37 +00:00
merge_overlapping_carets ( ) ;
2021-07-09 16:27:09 +00:00
}
// Minimap
2020-02-02 18:47:33 +00:00
void TextEdit : : _update_minimap_hover ( ) {
const Point2 mp = get_local_mouse_pos ( ) ;
2023-04-03 16:01:10 +00:00
const int xmargin_end = get_size ( ) . width - theme_cache . style_normal - > get_margin ( SIDE_RIGHT ) ;
2020-02-02 18:47:33 +00:00
const bool hovering_sidebar = mp . x > xmargin_end - minimap_width & & mp . x < xmargin_end ;
if ( ! hovering_sidebar ) {
if ( hovering_minimap ) {
// Only redraw if the hovering status changed.
hovering_minimap = false ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-02-02 18:47:33 +00:00
}
// Return early to avoid running the operations below when not needed.
return ;
}
2021-09-23 14:58:43 +00:00
const int row = get_minimap_line_at_pos ( mp ) ;
2020-02-02 18:47:33 +00:00
const bool new_hovering_minimap = row > = get_first_visible_line ( ) & & row < = get_last_full_visible_line ( ) ;
if ( new_hovering_minimap ! = hovering_minimap ) {
// Only redraw if the hovering status changed.
hovering_minimap = new_hovering_minimap ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-02-02 18:47:33 +00:00
}
}
2021-07-09 16:27:09 +00:00
void TextEdit : : _update_minimap_click ( ) {
2021-07-10 10:41:38 +00:00
Point2 mp = get_local_mouse_pos ( ) ;
2021-07-09 16:27:09 +00:00
2023-04-03 16:01:10 +00:00
int xmargin_end = get_size ( ) . width - theme_cache . style_normal - > get_margin ( SIDE_RIGHT ) ;
2024-05-08 14:29:12 +00:00
if ( ! dragging_minimap & & ( mp . x < xmargin_end - minimap_width | | mp . x > xmargin_end ) ) {
2021-07-09 16:27:09 +00:00
minimap_clicked = false ;
return ;
}
minimap_clicked = true ;
dragging_minimap = true ;
2021-09-23 14:58:43 +00:00
int row = get_minimap_line_at_pos ( mp ) ;
2021-07-09 16:27:09 +00:00
if ( row > = get_first_visible_line ( ) & & ( row < get_last_full_visible_line ( ) | | row > = ( text . size ( ) - 1 ) ) ) {
minimap_scroll_ratio = v_scroll - > get_as_ratio ( ) ;
minimap_scroll_click_pos = mp . y ;
can_drag_minimap = true ;
return ;
}
2021-07-10 10:41:38 +00:00
Point2i next_line = get_next_visible_line_index_offset_from ( row , 0 , - get_visible_line_count ( ) / 2 ) ;
2024-05-30 16:06:02 +00:00
int first_line = MAX ( 0 , row - next_line . x + 1 ) ;
2021-07-10 10:41:38 +00:00
double delta = get_scroll_pos_for_line ( first_line , next_line . y ) - get_v_scroll ( ) ;
2021-07-09 16:27:09 +00:00
if ( delta < 0 ) {
2023-02-17 13:50:46 +00:00
_scroll_up ( - delta , true ) ;
2021-07-09 16:27:09 +00:00
} else {
2023-02-17 13:50:46 +00:00
_scroll_down ( delta , true ) ;
2021-07-09 16:27:09 +00:00
}
}
void TextEdit : : _update_minimap_drag ( ) {
if ( ! can_drag_minimap ) {
return ;
}
int control_height = _get_control_height ( ) ;
int scroll_height = v_scroll - > get_max ( ) * ( minimap_char_size . y + minimap_line_spacing ) ;
if ( control_height > scroll_height ) {
control_height = scroll_height ;
}
2021-07-10 10:41:38 +00:00
Point2 mp = get_local_mouse_pos ( ) ;
2021-07-09 16:27:09 +00:00
double diff = ( mp . y - minimap_scroll_click_pos ) / control_height ;
v_scroll - > set_as_ratio ( minimap_scroll_ratio + diff ) ;
}
2021-07-10 10:41:38 +00:00
/* Gutters. */
void TextEdit : : _update_gutter_width ( ) {
gutters_width = 0 ;
for ( int i = 0 ; i < gutters . size ( ) ; i + + ) {
if ( gutters [ i ] . draw ) {
gutters_width + = gutters [ i ] . width ;
}
}
if ( gutters_width > 0 ) {
gutter_padding = 2 ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-07-10 10:41:38 +00:00
}
/* Syntax highlighting. */
Dictionary TextEdit : : _get_line_syntax_highlighting ( int p_line ) {
2024-04-05 16:36:18 +00:00
return ( syntax_highlighter . is_null ( ) | | setting_text ) ? Dictionary ( ) : syntax_highlighter - > get_line_syntax_highlighting ( p_line ) ;
2021-07-10 10:41:38 +00:00
}
2024-01-22 23:27:19 +00:00
/* Deprecated. */
# ifndef DISABLE_DEPRECATED
Vector < int > TextEdit : : get_caret_index_edit_order ( ) {
Vector < int > carets_order = get_sorted_carets ( ) ;
carets_order . reverse ( ) ;
return carets_order ;
}
void TextEdit : : adjust_carets_after_edit ( int p_caret , int p_from_line , int p_from_col , int p_to_line , int p_to_col ) {
}
int TextEdit : : get_selection_line ( int p_caret ) const {
return get_selection_origin_line ( p_caret ) ;
}
int TextEdit : : get_selection_column ( int p_caret ) const {
return get_selection_origin_column ( p_caret ) ;
}
# endif
2021-07-10 10:41:38 +00:00
/*** Super internal Core API. Everything builds on it. ***/
2024-01-22 23:26:16 +00:00
void TextEdit : : _text_changed ( ) {
_cancel_drag_and_drop_text ( ) ;
queue_redraw ( ) ;
if ( text_changed_dirty | | setting_text ) {
return ;
}
if ( is_inside_tree ( ) ) {
callable_mp ( this , & TextEdit : : _emit_text_changed ) . call_deferred ( ) ;
}
text_changed_dirty = true ;
}
void TextEdit : : _emit_text_changed ( ) {
2024-05-14 09:42:00 +00:00
emit_signal ( SceneStringName ( text_changed ) ) ;
2021-07-10 10:41:38 +00:00
text_changed_dirty = false ;
}
void TextEdit : : _insert_text ( int p_line , int p_char , const String & p_text , int * r_end_line , int * r_end_char ) {
if ( ! setting_text & & idle_detect - > is_inside_tree ( ) ) {
idle_detect - > start ( ) ;
}
if ( undo_enabled ) {
_clear_redo ( ) ;
}
int retline , retchar ;
_base_insert_text ( p_line , p_char , p_text , retline , retchar ) ;
if ( r_end_line ) {
* r_end_line = retline ;
}
if ( r_end_char ) {
* r_end_char = retchar ;
}
if ( ! undo_enabled ) {
return ;
}
/* UNDO!! */
TextOperation op ;
op . type = TextOperation : : TYPE_INSERT ;
op . from_line = p_line ;
op . from_column = p_char ;
op . to_line = retline ;
op . to_column = retchar ;
op . text = p_text ;
op . version = + + version ;
op . chain_forward = false ;
op . chain_backward = false ;
2023-12-13 13:38:20 +00:00
if ( next_operation_is_complex ) {
op . start_carets = current_op . start_carets ;
} else {
op . start_carets = carets ;
}
2022-06-03 16:47:37 +00:00
op . end_carets = carets ;
2021-07-10 10:41:38 +00:00
2023-11-24 21:52:01 +00:00
op . prev_version = get_version ( ) ;
_push_current_op ( ) ;
current_op = op ;
2021-07-10 10:41:38 +00:00
}
void TextEdit : : _remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
if ( ! setting_text & & idle_detect - > is_inside_tree ( ) ) {
idle_detect - > start ( ) ;
}
2022-09-29 09:53:28 +00:00
String txt ;
2021-07-10 10:41:38 +00:00
if ( undo_enabled ) {
_clear_redo ( ) ;
2022-09-29 09:53:28 +00:00
txt = _base_get_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
2021-07-10 10:41:38 +00:00
}
_base_remove_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
if ( ! undo_enabled ) {
return ;
}
/* UNDO! */
TextOperation op ;
op . type = TextOperation : : TYPE_REMOVE ;
op . from_line = p_from_line ;
op . from_column = p_from_column ;
op . to_line = p_to_line ;
op . to_column = p_to_column ;
2022-09-29 09:53:28 +00:00
op . text = txt ;
2021-07-10 10:41:38 +00:00
op . version = + + version ;
op . chain_forward = false ;
op . chain_backward = false ;
2023-12-13 13:38:20 +00:00
if ( next_operation_is_complex ) {
op . start_carets = current_op . start_carets ;
} else {
op . start_carets = carets ;
}
2022-06-03 16:47:37 +00:00
op . end_carets = carets ;
2021-07-10 10:41:38 +00:00
op . prev_version = get_version ( ) ;
_push_current_op ( ) ;
current_op = op ;
}
void TextEdit : : _base_insert_text ( int p_line , int p_char , const String & p_text , int & r_end_line , int & r_end_column ) {
// Save for undo.
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_COND ( p_char < 0 ) ;
/* STEP 1: Remove \r from source text and separate in substrings. */
2021-12-08 15:29:40 +00:00
const String text_to_insert = p_text . replace ( " \r " , " " ) ;
Vector < String > substrings = text_to_insert . split ( " \n " ) ;
2021-07-10 10:41:38 +00:00
// Is this just a new empty line?
2021-12-08 15:29:40 +00:00
bool shift_first_line = p_char = = 0 & & substrings . size ( ) = = 2 & & text_to_insert = = " \n " ;
2021-07-10 10:41:38 +00:00
/* STEP 2: Add spaces if the char is greater than the end of the line. */
while ( p_char > text [ p_line ] . length ( ) ) {
text . set ( p_line , text [ p_line ] + String : : chr ( ' ' ) , structured_text_parser ( st_parser , st_args , text [ p_line ] + String : : chr ( ' ' ) ) ) ;
}
/* STEP 3: Separate dest string in pre and post text. */
String postinsert_text = text [ p_line ] . substr ( p_char , text [ p_line ] . size ( ) ) ;
2021-12-08 15:29:40 +00:00
substrings . write [ 0 ] = text [ p_line ] . substr ( 0 , p_char ) + substrings [ 0 ] ;
substrings . write [ substrings . size ( ) - 1 ] + = postinsert_text ;
2021-07-10 10:41:38 +00:00
2021-12-08 15:29:40 +00:00
Vector < Array > bidi_override ;
bidi_override . resize ( substrings . size ( ) ) ;
for ( int i = 0 ; i < substrings . size ( ) ; i + + ) {
bidi_override . write [ i ] = structured_text_parser ( st_parser , st_args , substrings [ i ] ) ;
2021-07-10 10:41:38 +00:00
}
2021-12-08 15:29:40 +00:00
text . insert ( p_line , substrings , bidi_override ) ;
2021-07-10 10:41:38 +00:00
if ( shift_first_line ) {
text . move_gutters ( p_line , p_line + 1 ) ;
text . set_hidden ( p_line + 1 , text . is_hidden ( p_line ) ) ;
text . set_hidden ( p_line , false ) ;
}
r_end_line = p_line + substrings . size ( ) - 1 ;
r_end_column = text [ r_end_line ] . length ( ) - postinsert_text . length ( ) ;
2022-06-03 16:47:37 +00:00
TextServer : : Direction dir = TS - > shaped_text_get_dominant_direction_in_range ( text . get_line_data ( r_end_line ) - > get_rid ( ) , ( r_end_line = = p_line ) ? carets [ 0 ] . column : 0 , r_end_column ) ;
2021-07-10 10:41:38 +00:00
if ( dir ! = TextServer : : DIRECTION_AUTO ) {
input_direction = ( TextDirection ) dir ;
}
2024-01-22 23:26:16 +00:00
_text_changed ( ) ;
2021-07-10 10:41:38 +00:00
emit_signal ( SNAME ( " lines_edited_from " ) , p_line , r_end_line ) ;
}
String TextEdit : : _base_get_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) const {
ERR_FAIL_INDEX_V ( p_from_line , text . size ( ) , String ( ) ) ;
ERR_FAIL_INDEX_V ( p_from_column , text [ p_from_line ] . length ( ) + 1 , String ( ) ) ;
ERR_FAIL_INDEX_V ( p_to_line , text . size ( ) , String ( ) ) ;
ERR_FAIL_INDEX_V ( p_to_column , text [ p_to_line ] . length ( ) + 1 , String ( ) ) ;
ERR_FAIL_COND_V ( p_to_line < p_from_line , String ( ) ) ; // 'from > to'.
ERR_FAIL_COND_V ( p_to_line = = p_from_line & & p_to_column < p_from_column , String ( ) ) ; // 'from > to'.
2021-12-08 15:29:40 +00:00
StringBuilder ret ;
2021-07-10 10:41:38 +00:00
for ( int i = p_from_line ; i < = p_to_line ; i + + ) {
int begin = ( i = = p_from_line ) ? p_from_column : 0 ;
int end = ( i = = p_to_line ) ? p_to_column : text [ i ] . length ( ) ;
if ( i > p_from_line ) {
ret + = " \n " ;
}
ret + = text [ i ] . substr ( begin , end - begin ) ;
}
2021-12-08 15:29:40 +00:00
return ret . as_string ( ) ;
2021-07-10 10:41:38 +00:00
}
void TextEdit : : _base_remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
ERR_FAIL_INDEX ( p_from_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_from_column , text [ p_from_line ] . length ( ) + 1 ) ;
ERR_FAIL_INDEX ( p_to_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_to_column , text [ p_to_line ] . length ( ) + 1 ) ;
ERR_FAIL_COND ( p_to_line < p_from_line ) ; // 'from > to'.
ERR_FAIL_COND ( p_to_line = = p_from_line & & p_to_column < p_from_column ) ; // 'from > to'.
String pre_text = text [ p_from_line ] . substr ( 0 , p_from_column ) ;
String post_text = text [ p_to_line ] . substr ( p_to_column , text [ p_to_line ] . length ( ) ) ;
2021-12-08 15:29:40 +00:00
text . remove_range ( p_from_line , p_to_line ) ;
2021-07-10 10:41:38 +00:00
text . set ( p_from_line , pre_text + post_text , structured_text_parser ( st_parser , st_args , pre_text + post_text ) ) ;
2024-01-22 23:26:16 +00:00
_text_changed ( ) ;
2021-07-10 10:41:38 +00:00
emit_signal ( SNAME ( " lines_edited_from " ) , p_to_line , p_from_line ) ;
}
2022-03-04 08:18:44 +00:00
TextEdit : : TextEdit ( const String & p_placeholder ) {
2022-01-23 20:07:35 +00:00
placeholder_data_buf . instantiate ( ) ;
2022-06-03 16:47:37 +00:00
carets . push_back ( Caret ( ) ) ;
2022-01-23 20:07:35 +00:00
2015-01-02 18:08:40 +00:00
clear ( ) ;
set_focus_mode ( FOCUS_ALL ) ;
2018-09-11 16:25:40 +00:00
set_default_cursor_shape ( CURSOR_IBEAM ) ;
2022-01-11 13:59:52 +00:00
set_process_unhandled_key_input ( true ) ;
2015-06-07 01:06:58 +00:00
2021-06-15 14:05:01 +00:00
text . set_tab_size ( text . get_tab_size ( ) ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
h_scroll = memnew ( HScrollBar ) ;
v_scroll = memnew ( VScrollBar ) ;
2016-03-08 23:00:52 +00:00
2021-08-25 13:49:30 +00:00
add_child ( h_scroll , false , INTERNAL_MODE_FRONT ) ;
add_child ( v_scroll , false , INTERNAL_MODE_FRONT ) ;
2016-03-08 23:00:52 +00:00
2024-05-14 09:42:00 +00:00
h_scroll - > connect ( SceneStringName ( value_changed ) , callable_mp ( this , & TextEdit : : _scroll_moved ) ) ;
v_scroll - > connect ( SceneStringName ( value_changed ) , callable_mp ( this , & TextEdit : : _scroll_moved ) ) ;
2016-03-08 23:00:52 +00:00
2020-02-21 17:28:45 +00:00
v_scroll - > connect ( " scrolling " , callable_mp ( this , & TextEdit : : _v_scroll_input ) ) ;
2017-08-22 19:02:08 +00:00
2021-07-09 11:42:55 +00:00
/* Caret. */
2016-05-09 18:21:55 +00:00
caret_blink_timer = memnew ( Timer ) ;
2021-08-25 13:49:30 +00:00
add_child ( caret_blink_timer , false , INTERNAL_MODE_FRONT ) ;
2016-05-09 18:21:55 +00:00
caret_blink_timer - > set_wait_time ( 0.65 ) ;
2020-02-21 17:28:45 +00:00
caret_blink_timer - > connect ( " timeout " , callable_mp ( this , & TextEdit : : _toggle_draw_caret ) ) ;
2021-07-08 17:35:56 +00:00
set_caret_blink_enabled ( false ) ;
2016-05-09 18:21:55 +00:00
2021-07-09 11:42:55 +00:00
/* Selection. */
click_select_held = memnew ( Timer ) ;
2021-08-25 13:49:30 +00:00
add_child ( click_select_held , false , INTERNAL_MODE_FRONT ) ;
2021-07-09 11:42:55 +00:00
click_select_held - > set_wait_time ( 0.05 ) ;
click_select_held - > connect ( " timeout " , callable_mp ( this , & TextEdit : : _click_selection_held ) ) ;
2015-01-02 18:08:40 +00:00
idle_detect = memnew ( Timer ) ;
2021-08-25 13:49:30 +00:00
add_child ( idle_detect , false , INTERNAL_MODE_FRONT ) ;
2015-01-02 18:08:40 +00:00
idle_detect - > set_one_shot ( true ) ;
2017-01-05 12:16:00 +00:00
idle_detect - > set_wait_time ( GLOBAL_GET ( " gui/timers/text_edit_idle_detect_sec " ) ) ;
2020-02-21 17:28:45 +00:00
idle_detect - > connect ( " timeout " , callable_mp ( this , & TextEdit : : _push_current_op ) ) ;
2015-12-09 18:56:41 +00:00
2020-04-13 12:30:56 +00:00
undo_stack_max_size = GLOBAL_GET ( " gui/common/text_edit_undo_stack_max_size " ) ;
2017-03-05 15:44:50 +00:00
2022-03-04 08:18:44 +00:00
set_placeholder ( p_placeholder ) ;
2024-03-28 20:07:27 +00:00
set_clip_contents ( true ) ;
2021-07-09 13:05:52 +00:00
set_editable ( true ) ;
2014-02-10 01:10:30 +00:00
}