2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* rich_text_label.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* 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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "rich_text_label.h"
2018-09-15 00:55:22 +02:00
2022-04-12 08:58:04 +02:00
# include "core/input/input_map.h"
2018-11-10 03:44:58 +01:00
# include "core/math/math_defs.h"
2018-09-11 18:13:45 +02:00
# include "core/os/keyboard.h"
# include "core/os/os.h"
2021-11-21 19:26:15 +01:00
# include "label.h"
2017-03-05 16:44:50 +01:00
# include "scene/scene_string_names.h"
2020-03-03 14:36:29 +01:00
# include "servers/display_server.h"
2020-02-06 21:51:36 +01:00
2021-11-12 13:42:58 +01:00
# include "modules/modules_enabled.gen.h" // For regex.
2020-02-06 21:51:36 +01:00
# ifdef MODULE_REGEX_ENABLED
# include "modules/regex/regex.h"
# endif
2021-02-11 17:33:45 +01:00
RichTextLabel : : Item * RichTextLabel : : _get_next_item ( Item * p_item , bool p_free ) const {
2015-12-26 14:25:17 +01:00
if ( p_free ) {
if ( p_item - > subitems . size ( ) ) {
return p_item - > subitems . front ( ) - > get ( ) ;
} else if ( ! p_item - > parent ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2015-12-26 14:25:17 +01:00
} else if ( p_item - > E - > next ( ) ) {
return p_item - > E - > next ( ) - > get ( ) ;
} else {
//go up until something with a next is found
while ( p_item - > parent & & ! p_item - > E - > next ( ) ) {
2017-03-05 16:44:50 +01:00
p_item = p_item - > parent ;
2015-12-26 14:25:17 +01:00
}
2020-05-14 16:41:43 +02:00
if ( p_item - > parent ) {
2015-12-26 14:25:17 +01:00
return p_item - > E - > next ( ) - > get ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2015-12-26 14:25:17 +01:00
} else {
2017-03-05 16:44:50 +01:00
if ( p_item - > subitems . size ( ) & & p_item - > type ! = ITEM_TABLE ) {
2015-12-26 14:25:17 +01:00
return p_item - > subitems . front ( ) - > get ( ) ;
2017-03-05 16:44:50 +01:00
} else if ( p_item - > type = = ITEM_FRAME ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2015-12-26 14:25:17 +01:00
} else if ( p_item - > E - > next ( ) ) {
return p_item - > E - > next ( ) - > get ( ) ;
} else {
//go up until something with a next is found
2017-03-05 16:44:50 +01:00
while ( p_item - > type ! = ITEM_FRAME & & ! p_item - > E - > next ( ) ) {
p_item = p_item - > parent ;
2015-12-26 14:25:17 +01:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( p_item - > type ! = ITEM_FRAME ) {
2015-12-26 14:25:17 +01:00
return p_item - > E - > next ( ) - > get ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2015-12-26 14:25:17 +01:00
}
2014-02-10 02:10:30 +01:00
}
2020-04-02 01:20:12 +02:00
return nullptr ;
2014-02-10 02:10:30 +01:00
}
2021-02-11 17:33:45 +01:00
RichTextLabel : : Item * RichTextLabel : : _get_prev_item ( Item * p_item , bool p_free ) const {
2017-12-22 19:09:31 +01:00
if ( p_free ) {
if ( p_item - > subitems . size ( ) ) {
return p_item - > subitems . back ( ) - > get ( ) ;
} else if ( ! p_item - > parent ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2017-12-22 19:09:31 +01:00
} else if ( p_item - > E - > prev ( ) ) {
return p_item - > E - > prev ( ) - > get ( ) ;
} else {
//go back until something with a prev is found
while ( p_item - > parent & & ! p_item - > E - > prev ( ) ) {
p_item = p_item - > parent ;
}
2020-05-14 16:41:43 +02:00
if ( p_item - > parent ) {
2017-12-22 19:09:31 +01:00
return p_item - > E - > prev ( ) - > get ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2017-12-22 19:09:31 +01:00
}
} else {
if ( p_item - > subitems . size ( ) & & p_item - > type ! = ITEM_TABLE ) {
return p_item - > subitems . back ( ) - > get ( ) ;
} else if ( p_item - > type = = ITEM_FRAME ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2017-12-22 19:09:31 +01:00
} else if ( p_item - > E - > prev ( ) ) {
return p_item - > E - > prev ( ) - > get ( ) ;
} else {
//go back until something with a prev is found
while ( p_item - > type ! = ITEM_FRAME & & ! p_item - > E - > prev ( ) ) {
p_item = p_item - > parent ;
}
2020-05-14 16:41:43 +02:00
if ( p_item - > type ! = ITEM_FRAME ) {
2017-12-22 19:09:31 +01:00
return p_item - > E - > prev ( ) - > get ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2017-12-22 19:09:31 +01:00
}
}
2020-04-02 01:20:12 +02:00
return nullptr ;
2017-12-22 19:09:31 +01:00
}
2017-09-02 18:20:04 +02:00
Rect2 RichTextLabel : : _get_text_rect ( ) {
2022-09-01 17:52:49 +02:00
return Rect2 ( theme_cache . normal_style - > get_offset ( ) , get_size ( ) - theme_cache . normal_style - > get_minimum_size ( ) ) ;
2017-09-02 18:20:04 +02:00
}
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
RichTextLabel : : Item * RichTextLabel : : _get_item_at_pos ( RichTextLabel : : Item * p_item_from , RichTextLabel : : Item * p_item_to , int p_position ) {
int offset = 0 ;
for ( Item * it = p_item_from ; it & & it ! = p_item_to ; it = _get_next_item ( it ) ) {
switch ( it - > type ) {
case ITEM_TEXT : {
2022-04-05 12:40:26 +02:00
ItemText * t = static_cast < ItemText * > ( it ) ;
2020-10-02 14:02:02 +02:00
offset + = t - > text . length ( ) ;
2021-03-14 18:54:48 +01:00
if ( offset > p_position ) {
2020-10-02 14:02:02 +02:00
return it ;
}
} break ;
2022-07-06 14:16:50 +02:00
case ITEM_NEWLINE : {
offset + = 1 ;
if ( offset = = p_position ) {
return it ;
}
} break ;
2020-10-02 14:02:02 +02:00
case ITEM_IMAGE :
case ITEM_TABLE : {
offset + = 1 ;
} break ;
default :
break ;
}
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
return p_item_from ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
String RichTextLabel : : _roman ( int p_num , bool p_capitalize ) const {
if ( p_num > 3999 ) {
return " ERR " ;
} ;
String s ;
if ( p_capitalize ) {
2022-04-05 12:40:26 +02:00
const String M [ ] = { " " , " M " , " MM " , " MMM " } ;
const String C [ ] = { " " , " C " , " CC " , " CCC " , " CD " , " D " , " DC " , " DCC " , " DCCC " , " CM " } ;
const String X [ ] = { " " , " X " , " XX " , " XXX " , " XL " , " L " , " LX " , " LXX " , " LXXX " , " XC " } ;
const String I [ ] = { " " , " I " , " II " , " III " , " IV " , " V " , " VI " , " VII " , " VIII " , " IX " } ;
2020-10-02 14:02:02 +02:00
s = M [ p_num / 1000 ] + C [ ( p_num % 1000 ) / 100 ] + X [ ( p_num % 100 ) / 10 ] + I [ p_num % 10 ] ;
} else {
2022-04-05 12:40:26 +02:00
const String M [ ] = { " " , " m " , " mm " , " mmm " } ;
const String C [ ] = { " " , " c " , " cc " , " ccc " , " cd " , " d " , " dc " , " dcc " , " dccc " , " cm " } ;
const String X [ ] = { " " , " x " , " xx " , " xxx " , " xl " , " l " , " lx " , " lxx " , " lxxx " , " xc " } ;
const String I [ ] = { " " , " i " , " ii " , " iii " , " iv " , " v " , " vi " , " vii " , " viii " , " ix " } ;
2020-10-02 14:02:02 +02:00
s = M [ p_num / 1000 ] + C [ ( p_num % 1000 ) / 100 ] + X [ ( p_num % 100 ) / 10 ] + I [ p_num % 10 ] ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
return s ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
String RichTextLabel : : _letters ( int p_num , bool p_capitalize ) const {
int64_t n = p_num ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
int chars = 0 ;
do {
n / = 24 ;
chars + + ;
} while ( n ) ;
2016-10-17 21:03:47 +02:00
2020-10-02 14:02:02 +02:00
String s ;
s . resize ( chars + 1 ) ;
char32_t * c = s . ptrw ( ) ;
c [ chars ] = 0 ;
n = p_num ;
do {
int mod = ABS ( n % 24 ) ;
char a = ( p_capitalize ? ' A ' : ' a ' ) ;
c [ - - chars ] = a + mod - 1 ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
n / = 24 ;
} while ( n ) ;
2015-04-21 21:01:58 +02:00
2020-10-02 14:02:02 +02:00
return s ;
}
2014-02-10 02:10:30 +01:00
2022-01-20 08:30:42 +01:00
void RichTextLabel : : _update_line_font ( ItemFrame * p_frame , int p_line , const Ref < Font > & p_base_font , int p_base_font_size ) {
ERR_FAIL_COND ( p_frame = = nullptr ) ;
2022-05-18 09:17:55 +02:00
ERR_FAIL_COND ( p_line < 0 | | p_line > = ( int ) p_frame - > lines . size ( ) ) ;
2022-01-20 08:30:42 +01:00
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
MutexLock lock ( l . text_buf - > get_mutex ( ) ) ;
2022-01-20 08:30:42 +01:00
RID t = l . text_buf - > get_rid ( ) ;
int spans = TS - > shaped_get_span_count ( t ) ;
for ( int i = 0 ; i < spans ; i + + ) {
2022-04-05 12:40:26 +02:00
ItemText * it = reinterpret_cast < ItemText * > ( ( uint64_t ) TS - > shaped_get_span_meta ( t , i ) ) ;
2022-01-20 08:30:42 +01:00
if ( it ) {
2022-07-26 09:45:40 +02:00
Ref < Font > font = p_base_font ;
int font_size = p_base_font_size ;
ItemFont * font_it = _find_font ( it ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2022-01-20 08:30:42 +01:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( it ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2022-01-20 08:30:42 +01:00
}
2022-05-09 11:47:10 +02:00
TS - > shaped_set_span_update_font ( t , i , font - > get_rids ( ) , font_size , font - > get_opentype_features ( ) ) ;
for ( int j = 0 ; j < TextServer : : SPACING_MAX ; j + + ) {
TS - > shaped_text_set_spacing ( t , TextServer : : SpacingType ( j ) , font - > get_spacing ( TextServer : : SpacingType ( j ) ) ) ;
}
2022-01-20 08:30:42 +01:00
}
}
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2022-01-20 08:30:42 +01:00
for ( Item * it = l . from ; it & & it ! = it_to ; it = _get_next_item ( it ) ) {
switch ( it - > type ) {
case ITEM_TABLE : {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
for ( Item * E : table - > subitems ) {
ERR_CONTINUE ( E - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2022-05-18 09:17:55 +02:00
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
2022-01-20 08:30:42 +01:00
_update_line_font ( frame , i , p_base_font , p_base_font_size ) ;
}
}
} break ;
default :
break ;
}
}
}
2022-05-18 09:17:55 +02:00
float RichTextLabel : : _resize_line ( ItemFrame * p_frame , int p_line , const Ref < Font > & p_base_font , int p_base_font_size , int p_width , float p_h ) {
ERR_FAIL_COND_V ( p_frame = = nullptr , p_h ) ;
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = ( int ) p_frame - > lines . size ( ) , p_h ) ;
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
MutexLock lock ( l . text_buf - > get_mutex ( ) ) ;
2020-10-02 14:02:02 +02:00
l . offset . x = _find_margin ( l . from , p_base_font , p_base_font_size ) ;
l . text_buf - > set_width ( p_width - l . offset . x ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( tab_size > 0 ) { // Align inline tabs.
Vector < float > tabs ;
2022-05-09 11:47:10 +02:00
tabs . push_back ( tab_size * p_base_font - > get_char_size ( ' ' , p_base_font_size ) . width ) ;
2020-10-02 14:02:02 +02:00
l . text_buf - > tab_align ( tabs ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2020-10-02 14:02:02 +02:00
for ( Item * it = l . from ; it & & it ! = it_to ; it = _get_next_item ( it ) ) {
switch ( it - > type ) {
case ITEM_TABLE : {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
int col_count = table - > columns . size ( ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < col_count ; i + + ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width = 0 ;
2020-10-02 14:02:02 +02:00
}
2020-01-31 06:05:52 +01:00
2020-10-02 14:02:02 +02:00
int idx = 0 ;
2021-07-16 05:45:57 +02:00
for ( Item * E : table - > subitems ) {
ERR_CONTINUE ( E - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2022-05-18 09:17:55 +02:00
float prev_h = 0 ;
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
MutexLock sub_lock ( frame - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2022-01-28 11:27:47 +01:00
int w = _find_margin ( frame - > lines [ i ] . from , p_base_font , p_base_font_size ) + 1 ;
2022-05-18 09:17:55 +02:00
prev_h = _resize_line ( frame , i , p_base_font , p_base_font_size , w , prev_h ) ;
2020-10-02 14:02:02 +02:00
}
idx + + ;
}
2016-10-17 21:03:47 +02:00
2020-10-02 14:02:02 +02:00
// Compute minimum width for each cell.
2022-09-01 17:52:49 +02:00
const int available_width = p_width - theme_cache . table_h_separation * ( col_count - 1 ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
// Compute available width and total ratio (for expanders).
int total_ratio = 0 ;
int remaining_width = available_width ;
2022-09-01 17:52:49 +02:00
table - > total_width = theme_cache . table_h_separation ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < col_count ; i + + ) {
remaining_width - = table - > columns [ i ] . min_width ;
if ( table - > columns [ i ] . max_width > table - > columns [ i ] . min_width ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . expand = true ;
2020-10-02 14:02:02 +02:00
}
if ( table - > columns [ i ] . expand ) {
total_ratio + = table - > columns [ i ] . expand_ratio ;
}
}
2019-06-14 22:38:33 +02:00
2020-10-02 14:02:02 +02:00
// Assign actual widths.
for ( int i = 0 ; i < col_count ; i + + ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width = table - > columns [ i ] . min_width ;
2022-01-18 22:44:02 +01:00
if ( table - > columns [ i ] . expand & & total_ratio > 0 & & remaining_width > 0 ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width + = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
2020-10-02 14:02:02 +02:00
}
2022-09-01 17:52:49 +02:00
table - > total_width + = table - > columns [ i ] . width + theme_cache . table_h_separation ;
2020-10-02 14:02:02 +02:00
}
2019-06-14 22:38:33 +02:00
2020-10-02 14:02:02 +02:00
// Resize to max_width if needed and distribute the remaining space.
bool table_need_fit = true ;
while ( table_need_fit ) {
table_need_fit = false ;
// Fit slim.
for ( int i = 0 ; i < col_count ; i + + ) {
if ( ! table - > columns [ i ] . expand ) {
continue ;
}
int dif = table - > columns [ i ] . width - table - > columns [ i ] . max_width ;
if ( dif > 0 ) {
table_need_fit = true ;
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width = table - > columns [ i ] . max_width ;
2020-10-02 14:02:02 +02:00
table - > total_width - = dif ;
total_ratio - = table - > columns [ i ] . expand_ratio ;
}
}
// Grow.
remaining_width = available_width - table - > total_width ;
if ( remaining_width > 0 & & total_ratio > 0 ) {
for ( int i = 0 ; i < col_count ; i + + ) {
if ( table - > columns [ i ] . expand ) {
int dif = table - > columns [ i ] . max_width - table - > columns [ i ] . width ;
if ( dif > 0 ) {
int slice = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
int incr = MIN ( dif , slice ) ;
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width + = incr ;
2020-10-02 14:02:02 +02:00
table - > total_width + = incr ;
}
}
}
}
2019-06-20 12:42:25 +02:00
}
2020-10-02 14:02:02 +02:00
// Update line width and get total height.
idx = 0 ;
table - > total_height = 0 ;
table - > rows . clear ( ) ;
Vector2 offset ;
2021-02-09 18:24:36 +01:00
float row_height = 0.0 ;
2020-10-02 14:02:02 +02:00
2021-07-16 05:45:57 +02:00
for ( Item * E : table - > subitems ) {
ERR_CONTINUE ( E - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2020-10-02 14:02:02 +02:00
int column = idx % col_count ;
offset . x + = frame - > padding . position . x ;
float yofs = frame - > padding . position . y ;
2022-05-18 09:17:55 +02:00
float prev_h = 0 ;
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
MutexLock sub_lock ( frame - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
frame - > lines [ i ] . text_buf - > set_width ( table - > columns [ column ] . width ) ;
table - > columns [ column ] . width = MAX ( table - > columns [ column ] . width , ceil ( frame - > lines [ i ] . text_buf - > get_size ( ) . x ) ) ;
2020-10-02 14:02:02 +02:00
2022-05-18 09:17:55 +02:00
frame - > lines [ i ] . offset . y = prev_h ;
frame - > lines [ i ] . offset + = offset ;
2020-10-02 14:02:02 +02:00
2022-09-01 17:52:49 +02:00
float h = frame - > lines [ i ] . text_buf - > get_size ( ) . y + ( frame - > lines [ i ] . text_buf - > get_line_count ( ) - 1 ) * theme_cache . line_separation ;
2022-01-18 21:01:01 +01:00
if ( i > 0 ) {
2022-09-01 17:52:49 +02:00
h + = theme_cache . line_separation ;
2022-01-18 21:01:01 +01:00
}
2020-10-02 14:02:02 +02:00
if ( frame - > min_size_over . y > 0 ) {
h = MAX ( h , frame - > min_size_over . y ) ;
}
if ( frame - > max_size_over . y > 0 ) {
h = MIN ( h , frame - > max_size_over . y ) ;
}
yofs + = h ;
2022-09-01 17:52:49 +02:00
prev_h = frame - > lines [ i ] . offset . y + frame - > lines [ i ] . text_buf - > get_size ( ) . y + frame - > lines [ i ] . text_buf - > get_line_count ( ) * theme_cache . line_separation ;
2020-10-02 14:02:02 +02:00
}
yofs + = frame - > padding . size . y ;
2022-09-01 17:52:49 +02:00
offset . x + = table - > columns [ column ] . width + theme_cache . table_h_separation + frame - > padding . size . x ;
2020-10-02 14:02:02 +02:00
row_height = MAX ( yofs , row_height ) ;
if ( column = = col_count - 1 ) {
offset . x = 0 ;
2022-09-01 17:52:49 +02:00
row_height + = theme_cache . table_v_separation ;
2020-10-02 14:02:02 +02:00
table - > total_height + = row_height ;
offset . y + = row_height ;
table - > rows . push_back ( row_height ) ;
row_height = 0 ;
}
idx + + ;
}
l . text_buf - > resize_object ( ( uint64_t ) it , Size2 ( table - > total_width , table - > total_height ) , table - > inline_align ) ;
2019-06-14 22:38:33 +02:00
} break ;
2020-10-02 14:02:02 +02:00
default :
break ;
}
}
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
l . offset . y = p_h ;
2022-06-05 12:58:04 +02:00
return _calculate_line_vertical_offset ( l ) ;
2020-10-02 14:02:02 +02:00
}
2022-05-18 09:17:55 +02:00
float RichTextLabel : : _shape_line ( ItemFrame * p_frame , int p_line , const Ref < Font > & p_base_font , int p_base_font_size , int p_width , float p_h , int * r_char_offset ) {
ERR_FAIL_COND_V ( p_frame = = nullptr , p_h ) ;
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = ( int ) p_frame - > lines . size ( ) , p_h ) ;
2020-10-02 14:02:02 +02:00
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
MutexLock lock ( l . text_buf - > get_mutex ( ) ) ;
2020-10-02 14:02:02 +02:00
2022-07-11 11:40:31 +02:00
BitField < TextServer : : LineBreakFlag > autowrap_flags = TextServer : : BREAK_MANDATORY ;
2022-02-03 14:56:44 +01:00
switch ( autowrap_mode ) {
2022-06-15 10:01:45 +02:00
case TextServer : : AUTOWRAP_WORD_SMART :
2022-07-11 11:40:31 +02:00
autowrap_flags = TextServer : : BREAK_WORD_BOUND | TextServer : : BREAK_ADAPTIVE | TextServer : : BREAK_MANDATORY ;
2022-02-03 14:56:44 +01:00
break ;
2022-06-15 10:01:45 +02:00
case TextServer : : AUTOWRAP_WORD :
2022-02-03 14:56:44 +01:00
autowrap_flags = TextServer : : BREAK_WORD_BOUND | TextServer : : BREAK_MANDATORY ;
break ;
2022-06-15 10:01:45 +02:00
case TextServer : : AUTOWRAP_ARBITRARY :
2022-02-03 14:56:44 +01:00
autowrap_flags = TextServer : : BREAK_GRAPHEME_BOUND | TextServer : : BREAK_MANDATORY ;
break ;
2022-06-15 10:01:45 +02:00
case TextServer : : AUTOWRAP_OFF :
2022-02-03 14:56:44 +01:00
break ;
}
2022-08-30 10:56:17 +02:00
autowrap_flags = autowrap_flags | TextServer : : BREAK_TRIM_EDGE_SPACES ;
2022-02-03 14:56:44 +01:00
2020-10-02 14:02:02 +02:00
// Clear cache.
l . text_buf - > clear ( ) ;
2022-07-11 11:40:31 +02:00
l . text_buf - > set_break_flags ( autowrap_flags ) ;
l . text_buf - > set_justification_flags ( TextServer : : JUSTIFICATION_KASHIDA | TextServer : : JUSTIFICATION_WORD_BOUND | TextServer : : JUSTIFICATION_TRIM_EDGE_SPACES ) ;
2020-10-02 14:02:02 +02:00
l . char_offset = * r_char_offset ;
l . char_count = 0 ;
// Add indent.
l . offset . x = _find_margin ( l . from , p_base_font , p_base_font_size ) ;
l . text_buf - > set_width ( p_width - l . offset . x ) ;
2021-11-25 03:58:47 +01:00
l . text_buf - > set_alignment ( _find_alignment ( l . from ) ) ;
2020-10-02 14:02:02 +02:00
l . text_buf - > set_direction ( _find_direction ( l . from ) ) ;
if ( tab_size > 0 ) { // Align inline tabs.
Vector < float > tabs ;
2022-05-09 11:47:10 +02:00
tabs . push_back ( tab_size * p_base_font - > get_char_size ( ' ' , p_base_font_size ) . width ) ;
2020-10-02 14:02:02 +02:00
l . text_buf - > tab_align ( tabs ) ;
}
// Shape current paragraph.
String text ;
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2021-09-20 08:35:24 +02:00
int remaining_characters = visible_characters - l . char_offset ;
2020-10-02 14:02:02 +02:00
for ( Item * it = l . from ; it & & it ! = it_to ; it = _get_next_item ( it ) ) {
2022-06-15 10:01:45 +02:00
if ( visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING & & visible_characters > = 0 & & remaining_characters < = 0 ) {
2020-10-02 14:02:02 +02:00
break ;
}
switch ( it - > type ) {
2020-11-19 15:45:23 +01:00
case ITEM_DROPCAP : {
// Add dropcap.
2022-04-05 12:40:26 +02:00
const ItemDropcap * dc = static_cast < ItemDropcap * > ( it ) ;
l . text_buf - > set_dropcap ( dc - > text , dc - > font , dc - > font_size , dc - > dropcap_margins ) ;
l . dc_color = dc - > color ;
l . dc_ol_size = dc - > ol_size ;
l . dc_ol_color = dc - > ol_color ;
2020-11-19 15:45:23 +01:00
} break ;
2020-10-02 14:02:02 +02:00
case ITEM_NEWLINE : {
2022-07-26 09:45:40 +02:00
Ref < Font > font = p_base_font ;
int font_size = p_base_font_size ;
ItemFont * font_it = _find_font ( it ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2020-10-02 14:02:02 +02:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( it ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2020-10-02 14:02:02 +02:00
}
2022-05-09 11:47:10 +02:00
l . text_buf - > add_string ( " \n " , font , font_size ) ;
2020-10-02 14:02:02 +02:00
text + = " \n " ;
2021-09-20 08:35:24 +02:00
l . char_count + + ;
remaining_characters - - ;
2020-10-02 14:02:02 +02:00
} break ;
case ITEM_TEXT : {
2022-04-05 12:40:26 +02:00
ItemText * t = static_cast < ItemText * > ( it ) ;
2022-07-26 09:45:40 +02:00
Ref < Font > font = p_base_font ;
int font_size = p_base_font_size ;
ItemFont * font_it = _find_font ( it ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2020-05-14 16:41:43 +02:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( it ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2020-10-02 14:02:02 +02:00
}
String lang = _find_language ( it ) ;
String tx = t - > text ;
2022-06-15 10:01:45 +02:00
if ( visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING & & visible_characters > = 0 & & remaining_characters > = 0 ) {
2021-09-20 08:35:24 +02:00
tx = tx . substr ( 0 , remaining_characters ) ;
2020-10-02 14:02:02 +02:00
}
2021-09-20 08:35:24 +02:00
remaining_characters - = tx . length ( ) ;
2014-02-10 02:10:30 +01:00
2022-05-09 11:47:10 +02:00
l . text_buf - > add_string ( tx , font , font_size , lang , ( uint64_t ) it ) ;
2020-10-02 14:02:02 +02:00
text + = tx ;
l . char_count + = tx . length ( ) ;
} break ;
case ITEM_IMAGE : {
2022-04-05 12:40:26 +02:00
ItemImage * img = static_cast < ItemImage * > ( it ) ;
2022-02-12 10:48:56 +01:00
l . text_buf - > add_object ( ( uint64_t ) it , img - > size , img - > inline_align , 1 ) ;
2020-10-02 14:02:02 +02:00
text + = String : : chr ( 0xfffc ) ;
2021-09-20 08:35:24 +02:00
l . char_count + + ;
remaining_characters - - ;
2020-10-02 14:02:02 +02:00
} break ;
case ITEM_TABLE : {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
int col_count = table - > columns . size ( ) ;
int t_char_count = 0 ;
// Set minimums to zero.
for ( int i = 0 ; i < col_count ; i + + ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . min_width = 0 ;
table - > columns [ i ] . max_width = 0 ;
table - > columns [ i ] . width = 0 ;
2020-10-02 14:02:02 +02:00
}
// Compute minimum width for each cell.
2022-09-01 17:52:49 +02:00
const int available_width = p_width - theme_cache . table_h_separation * ( col_count - 1 ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
int idx = 0 ;
2021-07-16 05:45:57 +02:00
for ( Item * E : table - > subitems ) {
ERR_CONTINUE ( E - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
int column = idx % col_count ;
2022-05-18 09:17:55 +02:00
float prev_h = 0 ;
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
MutexLock sub_lock ( frame - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2020-10-02 14:02:02 +02:00
int char_offset = l . char_offset + l . char_count ;
2022-01-28 11:27:47 +01:00
int w = _find_margin ( frame - > lines [ i ] . from , p_base_font , p_base_font_size ) + 1 ;
2022-05-18 09:17:55 +02:00
prev_h = _shape_line ( frame , i , p_base_font , p_base_font_size , w , prev_h , & char_offset ) ;
2020-10-02 14:02:02 +02:00
int cell_ch = ( char_offset - ( l . char_offset + l . char_count ) ) ;
l . char_count + = cell_ch ;
t_char_count + = cell_ch ;
2021-09-20 08:35:24 +02:00
remaining_characters - = cell_ch ;
2020-10-02 14:02:02 +02:00
2022-05-18 09:17:55 +02:00
table - > columns [ column ] . min_width = MAX ( table - > columns [ column ] . min_width , ceil ( frame - > lines [ i ] . text_buf - > get_size ( ) . x ) ) ;
table - > columns [ column ] . max_width = MAX ( table - > columns [ column ] . max_width , ceil ( frame - > lines [ i ] . text_buf - > get_non_wrapped_size ( ) . x ) ) ;
2020-10-02 14:02:02 +02:00
}
idx + + ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
// Compute available width and total ratio (for expanders).
int total_ratio = 0 ;
int remaining_width = available_width ;
2022-09-01 17:52:49 +02:00
table - > total_width = theme_cache . table_h_separation ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < col_count ; i + + ) {
remaining_width - = table - > columns [ i ] . min_width ;
if ( table - > columns [ i ] . max_width > table - > columns [ i ] . min_width ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . expand = true ;
2020-10-02 14:02:02 +02:00
}
if ( table - > columns [ i ] . expand ) {
total_ratio + = table - > columns [ i ] . expand_ratio ;
}
}
2020-01-31 06:05:52 +01:00
2020-10-02 14:02:02 +02:00
// Assign actual widths.
for ( int i = 0 ; i < col_count ; i + + ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width = table - > columns [ i ] . min_width ;
2022-01-18 22:44:02 +01:00
if ( table - > columns [ i ] . expand & & total_ratio > 0 & & remaining_width > 0 ) {
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width + = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
2014-02-10 02:10:30 +01:00
}
2022-09-01 17:52:49 +02:00
table - > total_width + = table - > columns [ i ] . width + theme_cache . table_h_separation ;
2020-10-02 14:02:02 +02:00
}
2016-06-14 13:09:27 +02:00
2020-10-02 14:02:02 +02:00
// Resize to max_width if needed and distribute the remaining space.
bool table_need_fit = true ;
while ( table_need_fit ) {
table_need_fit = false ;
// Fit slim.
for ( int i = 0 ; i < col_count ; i + + ) {
if ( ! table - > columns [ i ] . expand ) {
continue ;
}
int dif = table - > columns [ i ] . width - table - > columns [ i ] . max_width ;
if ( dif > 0 ) {
table_need_fit = true ;
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width = table - > columns [ i ] . max_width ;
2020-10-02 14:02:02 +02:00
table - > total_width - = dif ;
total_ratio - = table - > columns [ i ] . expand_ratio ;
2016-06-14 13:09:27 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
// Grow.
remaining_width = available_width - table - > total_width ;
if ( remaining_width > 0 & & total_ratio > 0 ) {
for ( int i = 0 ; i < col_count ; i + + ) {
if ( table - > columns [ i ] . expand ) {
int dif = table - > columns [ i ] . max_width - table - > columns [ i ] . width ;
if ( dif > 0 ) {
int slice = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
int incr = MIN ( dif , slice ) ;
2022-05-18 09:17:55 +02:00
table - > columns [ i ] . width + = incr ;
2020-10-02 14:02:02 +02:00
table - > total_width + = incr ;
}
2015-12-28 05:40:23 +01:00
}
2015-04-21 21:01:58 +02:00
}
}
2020-10-02 14:02:02 +02:00
}
2015-04-21 21:01:58 +02:00
2020-10-02 14:02:02 +02:00
// Update line width and get total height.
idx = 0 ;
table - > total_height = 0 ;
table - > rows . clear ( ) ;
2015-04-21 21:01:58 +02:00
2020-10-02 14:02:02 +02:00
Vector2 offset ;
2021-02-09 18:24:36 +01:00
float row_height = 0.0 ;
2014-02-10 02:10:30 +01:00
2021-07-16 05:45:57 +02:00
for ( const List < Item * > : : Element * E = table - > subitems . front ( ) ; E ; E = E - > next ( ) ) {
2020-10-02 14:02:02 +02:00
ERR_CONTINUE ( E - > get ( ) - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E - > get ( ) ) ;
2015-04-21 21:01:58 +02:00
2020-10-02 14:02:02 +02:00
int column = idx % col_count ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
offset . x + = frame - > padding . position . x ;
float yofs = frame - > padding . position . y ;
2022-05-18 09:17:55 +02:00
float prev_h = 0 ;
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
MutexLock sub_lock ( frame - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
frame - > lines [ i ] . text_buf - > set_width ( table - > columns [ column ] . width ) ;
table - > columns [ column ] . width = MAX ( table - > columns [ column ] . width , ceil ( frame - > lines [ i ] . text_buf - > get_size ( ) . x ) ) ;
frame - > lines [ i ] . offset . y = prev_h ;
frame - > lines [ i ] . offset + = offset ;
2014-02-10 02:10:30 +01:00
2022-09-01 17:52:49 +02:00
float h = frame - > lines [ i ] . text_buf - > get_size ( ) . y + ( frame - > lines [ i ] . text_buf - > get_line_count ( ) - 1 ) * theme_cache . line_separation ;
2022-01-18 21:01:01 +01:00
if ( i > 0 ) {
2022-09-01 17:52:49 +02:00
h + = theme_cache . line_separation ;
2022-01-18 21:01:01 +01:00
}
2020-10-02 14:02:02 +02:00
if ( frame - > min_size_over . y > 0 ) {
h = MAX ( h , frame - > min_size_over . y ) ;
}
if ( frame - > max_size_over . y > 0 ) {
h = MIN ( h , frame - > max_size_over . y ) ;
}
yofs + = h ;
2022-09-01 17:52:49 +02:00
prev_h = frame - > lines [ i ] . offset . y + frame - > lines [ i ] . text_buf - > get_size ( ) . y + frame - > lines [ i ] . text_buf - > get_line_count ( ) * theme_cache . line_separation ;
2020-10-02 14:02:02 +02:00
}
yofs + = frame - > padding . size . y ;
2022-09-01 17:52:49 +02:00
offset . x + = table - > columns [ column ] . width + theme_cache . table_h_separation + frame - > padding . size . x ;
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
row_height = MAX ( yofs , row_height ) ;
2021-03-16 10:08:31 +01:00
// Add row height after last column of the row or last cell of the table.
if ( column = = col_count - 1 | | E - > next ( ) = = nullptr ) {
2020-10-02 14:02:02 +02:00
offset . x = 0 ;
2022-09-01 17:52:49 +02:00
row_height + = theme_cache . table_v_separation ;
2020-10-02 14:02:02 +02:00
table - > total_height + = row_height ;
offset . y + = row_height ;
table - > rows . push_back ( row_height ) ;
row_height = 0 ;
}
idx + + ;
}
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
l . text_buf - > add_object ( ( uint64_t ) it , Size2 ( table - > total_width , table - > total_height ) , table - > inline_align , t_char_count ) ;
text + = String : : chr ( 0xfffc ) . repeat ( t_char_count ) ;
} break ;
default :
break ;
}
}
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
//Apply BiDi override.
l . text_buf - > set_bidi_override ( structured_text_parser ( _find_stt ( l . from ) , st_args , text ) ) ;
2020-01-31 06:05:52 +01:00
2020-10-02 14:02:02 +02:00
* r_char_offset = l . char_offset + l . char_count ;
2019-09-25 22:05:42 +02:00
2022-05-18 09:17:55 +02:00
l . offset . y = p_h ;
2022-06-05 12:58:04 +02:00
return _calculate_line_vertical_offset ( l ) ;
2020-10-02 14:02:02 +02:00
}
2019-09-25 22:05:42 +02:00
2021-12-05 13:28:32 +01:00
int RichTextLabel : : _draw_line ( ItemFrame * p_frame , int p_line , const Vector2 & p_ofs , int p_width , const Color & p_base_color , int p_outline_size , const Color & p_outline_color , const Color & p_font_shadow_color , int p_shadow_outline_size , const Point2 & p_shadow_ofs , int & r_processed_glyphs ) {
2021-01-12 13:03:10 +01:00
ERR_FAIL_COND_V ( p_frame = = nullptr , 0 ) ;
2022-05-18 09:17:55 +02:00
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = ( int ) p_frame - > lines . size ( ) , 0 ) ;
2018-11-10 03:44:58 +01:00
2022-01-18 21:01:01 +01:00
Vector2 off ;
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
MutexLock lock ( l . text_buf - > get_mutex ( ) ) ;
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
Item * it_from = l . from ;
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2015-06-02 00:42:34 +02:00
2020-10-02 14:02:02 +02:00
if ( it_from = = nullptr ) {
2021-01-12 13:03:10 +01:00
return 0 ;
2020-10-02 14:02:02 +02:00
}
2016-10-17 21:03:47 +02:00
2020-10-02 14:02:02 +02:00
RID ci = get_canvas_item ( ) ;
bool rtl = ( l . text_buf - > get_direction ( ) = = TextServer : : DIRECTION_RTL ) ;
bool lrtl = is_layout_rtl ( ) ;
2014-02-10 02:10:30 +01:00
2022-06-15 10:01:45 +02:00
bool trim_chars = ( visible_characters > = 0 ) & & ( visible_chars_behavior = = TextServer : : VC_CHARS_AFTER_SHAPING ) ;
bool trim_glyphs_ltr = ( visible_characters > = 0 ) & & ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_LTR ) | | ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_AUTO ) & & ! lrtl ) ) ;
bool trim_glyphs_rtl = ( visible_characters > = 0 ) & & ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_RTL ) | | ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_AUTO ) & & lrtl ) ) ;
2021-12-05 13:28:32 +01:00
int total_glyphs = ( trim_glyphs_ltr | | trim_glyphs_rtl ) ? get_total_glyph_count ( ) : 0 ;
2022-08-20 20:06:13 +02:00
int visible_glyphs = total_glyphs * visible_ratio ;
2021-12-05 13:28:32 +01:00
2020-10-02 14:02:02 +02:00
Vector < int > list_index ;
Vector < ItemList * > list_items ;
_find_list ( l . from , list_index , list_items ) ;
String prefix ;
for ( int i = 0 ; i < list_index . size ( ) ; i + + ) {
if ( rtl ) {
prefix = prefix + " . " ;
} else {
prefix = " . " + prefix ;
}
String segment ;
if ( list_items [ i ] - > list_type = = LIST_DOTS ) {
static const char32_t _prefix [ 2 ] = { 0x25CF , 0 } ;
prefix = _prefix ;
break ;
} else if ( list_items [ i ] - > list_type = = LIST_NUMBERS ) {
segment = TS - > format_number ( itos ( list_index [ i ] ) , _find_language ( l . from ) ) ;
} else if ( list_items [ i ] - > list_type = = LIST_LETTERS ) {
segment = _letters ( list_index [ i ] , list_items [ i ] - > capitalize ) ;
} else if ( list_items [ i ] - > list_type = = LIST_ROMAN ) {
segment = _roman ( list_index [ i ] , list_items [ i ] - > capitalize ) ;
}
if ( rtl ) {
prefix = prefix + segment ;
} else {
prefix = segment + prefix ;
}
}
2021-12-09 10:42:46 +01:00
if ( ! prefix . is_empty ( ) ) {
2022-09-01 17:52:49 +02:00
Ref < Font > font = theme_cache . normal_font ;
int font_size = theme_cache . normal_font_size ;
2022-07-26 09:45:40 +02:00
ItemFont * font_it = _find_font ( l . from ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2020-10-02 14:02:02 +02:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( l . from ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2020-10-02 14:02:02 +02:00
}
if ( rtl ) {
float offx = 0.0f ;
if ( ! lrtl & & p_frame = = main ) { // Skip Scrollbar.
offx - = scroll_w ;
}
2021-11-25 03:58:47 +01:00
font - > draw_string ( ci , p_ofs + Vector2 ( p_width - l . offset . x + offx , l . text_buf - > get_line_ascent ( 0 ) ) , " " + prefix , HORIZONTAL_ALIGNMENT_LEFT , l . offset . x , font_size , _find_color ( l . from , p_base_color ) ) ;
2020-10-02 14:02:02 +02:00
} else {
float offx = 0.0f ;
if ( lrtl & & p_frame = = main ) { // Skip Scrollbar.
offx + = scroll_w ;
}
2021-11-25 03:58:47 +01:00
font - > draw_string ( ci , p_ofs + Vector2 ( offx , l . text_buf - > get_line_ascent ( 0 ) ) , prefix + " " , HORIZONTAL_ALIGNMENT_RIGHT , l . offset . x , font_size , _find_color ( l . from , p_base_color ) ) ;
2020-10-02 14:02:02 +02:00
}
}
2014-02-10 02:10:30 +01:00
2020-11-19 15:45:23 +01:00
// Draw dropcap.
int dc_lines = l . text_buf - > get_dropcap_lines ( ) ;
float h_off = l . text_buf - > get_dropcap_size ( ) . x ;
if ( l . dc_ol_size > 0 ) {
l . text_buf - > draw_dropcap_outline ( ci , p_ofs + ( ( rtl ) ? Vector2 ( ) : Vector2 ( l . offset . x , 0 ) ) , l . dc_ol_size , l . dc_ol_color ) ;
}
l . text_buf - > draw_dropcap ( ci , p_ofs + ( ( rtl ) ? Vector2 ( ) : Vector2 ( l . offset . x , 0 ) ) , l . dc_color ) ;
2021-01-12 13:03:10 +01:00
int line_count = 0 ;
Size2 ctrl_size = get_size ( ) ;
2020-10-02 14:02:02 +02:00
// Draw text.
for ( int line = 0 ; line < l . text_buf - > get_line_count ( ) ; line + + ) {
2022-01-18 21:01:01 +01:00
if ( line > 0 ) {
2022-09-01 17:52:49 +02:00
off . y + = theme_cache . line_separation ;
2022-01-18 21:01:01 +01:00
}
2021-01-12 13:03:10 +01:00
if ( p_ofs . y + off . y > = ctrl_size . height ) {
break ;
}
2022-06-09 20:04:21 +02:00
const Size2 line_size = l . text_buf - > get_line_size ( line ) ;
if ( p_ofs . y + off . y + line_size . y < = 0 ) {
off . y + = line_size . y ;
2021-01-12 13:03:10 +01:00
continue ;
}
2018-04-11 15:08:56 +02:00
2020-10-02 14:02:02 +02:00
float width = l . text_buf - > get_width ( ) ;
2022-06-09 20:04:21 +02:00
float length = line_size . x ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
// Draw line.
2021-01-12 13:03:10 +01:00
line_count + + ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( rtl ) {
off . x = p_width - l . offset . x - width ;
if ( ! lrtl & & p_frame = = main ) { // Skip Scrollbar.
off . x - = scroll_w ;
}
} else {
off . x = l . offset . x ;
if ( lrtl & & p_frame = = main ) { // Skip Scrollbar.
off . x + = scroll_w ;
}
}
// Draw text.
2021-11-25 03:58:47 +01:00
switch ( l . text_buf - > get_alignment ( ) ) {
case HORIZONTAL_ALIGNMENT_FILL :
case HORIZONTAL_ALIGNMENT_LEFT : {
2020-10-02 14:02:02 +02:00
if ( rtl ) {
off . x + = width - length ;
}
} break ;
2021-11-25 03:58:47 +01:00
case HORIZONTAL_ALIGNMENT_CENTER : {
2020-10-02 14:02:02 +02:00
off . x + = Math : : floor ( ( width - length ) / 2.0 ) ;
} break ;
2021-11-25 03:58:47 +01:00
case HORIZONTAL_ALIGNMENT_RIGHT : {
2020-10-02 14:02:02 +02:00
if ( ! rtl ) {
off . x + = width - length ;
}
} break ;
}
2020-11-19 15:45:23 +01:00
if ( line < = dc_lines ) {
if ( rtl ) {
off . x - = h_off ;
} else {
off . x + = h_off ;
}
}
2022-06-09 20:04:21 +02:00
RID rid = l . text_buf - > get_line_rid ( line ) ;
2020-10-02 14:02:02 +02:00
//draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
2022-05-09 11:47:10 +02:00
off . y + = TS - > shaped_text_get_ascent ( rid ) ;
2020-10-02 14:02:02 +02:00
// Draw inlined objects.
Array objects = TS - > shaped_text_get_objects ( rid ) ;
for ( int i = 0 ; i < objects . size ( ) ; i + + ) {
2022-04-05 12:40:26 +02:00
Item * it = reinterpret_cast < Item * > ( ( uint64_t ) objects [ i ] ) ;
2020-10-02 14:02:02 +02:00
if ( it ! = nullptr ) {
Rect2 rect = TS - > shaped_text_get_object_rect ( rid , objects [ i ] ) ;
//draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS
switch ( it - > type ) {
case ITEM_IMAGE : {
ItemImage * img = static_cast < ItemImage * > ( it ) ;
img - > image - > draw_rect ( ci , Rect2 ( p_ofs + rect . position + off , rect . size ) , false , img - > color ) ;
} break ;
case ITEM_TABLE : {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
2022-09-01 17:52:49 +02:00
Color odd_row_bg = theme_cache . table_odd_row_bg ;
Color even_row_bg = theme_cache . table_even_row_bg ;
Color border = theme_cache . table_border ;
int hseparation = theme_cache . table_h_separation ;
2020-10-02 14:02:02 +02:00
int col_count = table - > columns . size ( ) ;
int row_count = table - > rows . size ( ) ;
int idx = 0 ;
2021-07-16 05:45:57 +02:00
for ( Item * E : table - > subitems ) {
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2020-10-02 14:02:02 +02:00
int col = idx % col_count ;
int row = idx / col_count ;
if ( frame - > lines . size ( ) ! = 0 & & row < row_count ) {
Vector2 coff = frame - > lines [ 0 ] . offset ;
if ( rtl ) {
coff . x = rect . size . width - table - > columns [ col ] . width - coff . x ;
}
if ( row % 2 = = 0 ) {
2020-12-27 10:52:22 +01:00
draw_rect ( Rect2 ( p_ofs + rect . position + off + coff - frame - > padding . position , Size2 ( table - > columns [ col ] . width + hseparation + frame - > padding . position . x + frame - > padding . size . x , table - > rows [ row ] ) ) , ( frame - > odd_row_bg ! = Color ( 0 , 0 , 0 , 0 ) ? frame - > odd_row_bg : odd_row_bg ) , true ) ;
2020-10-02 14:02:02 +02:00
} else {
2020-12-27 10:52:22 +01:00
draw_rect ( Rect2 ( p_ofs + rect . position + off + coff - frame - > padding . position , Size2 ( table - > columns [ col ] . width + hseparation + frame - > padding . position . x + frame - > padding . size . x , table - > rows [ row ] ) ) , ( frame - > even_row_bg ! = Color ( 0 , 0 , 0 , 0 ) ? frame - > even_row_bg : even_row_bg ) , true ) ;
2015-05-09 23:09:58 +02:00
}
2020-12-27 10:52:22 +01:00
draw_rect ( Rect2 ( p_ofs + rect . position + off + coff - frame - > padding . position , Size2 ( table - > columns [ col ] . width + hseparation + frame - > padding . position . x + frame - > padding . size . x , table - > rows [ row ] ) ) , ( frame - > border ! = Color ( 0 , 0 , 0 , 0 ) ? frame - > border : border ) , false ) ;
2020-10-02 14:02:02 +02:00
}
2015-05-09 23:09:58 +02:00
2022-05-18 09:17:55 +02:00
for ( int j = 0 ; j < ( int ) frame - > lines . size ( ) ; j + + ) {
2021-12-05 13:28:32 +01:00
_draw_line ( frame , j , p_ofs + rect . position + off + Vector2 ( 0 , frame - > lines [ j ] . offset . y ) , rect . size . x , p_base_color , p_outline_size , p_outline_color , p_font_shadow_color , p_shadow_outline_size , p_shadow_ofs , r_processed_glyphs ) ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
idx + + ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
} break ;
default :
break ;
}
}
}
2018-01-04 12:10:29 +01:00
2021-08-27 23:19:51 +02:00
const Glyph * glyphs = TS - > shaped_text_get_glyphs ( rid ) ;
int gl_size = TS - > shaped_text_get_glyph_count ( rid ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Vector2 gloff = off ;
// Draw oulines and shadow.
2021-12-05 13:28:32 +01:00
int processed_glyphs_ol = r_processed_glyphs ;
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < gl_size ; i + + ) {
Item * it = _get_item_at_pos ( it_from , it_to , glyphs [ i ] . start ) ;
2020-12-25 22:45:28 +01:00
int size = _find_outline_size ( it , p_outline_size ) ;
2022-03-13 12:57:56 +01:00
Color font_color = _find_color ( it , p_base_color ) ;
Color font_outline_color = _find_outline_color ( it , p_outline_color ) ;
2021-11-05 23:28:48 +01:00
Color font_shadow_color = p_font_shadow_color ;
2022-03-13 12:57:56 +01:00
if ( ( size < = 0 | | font_outline_color . a = = 0 ) & & ( font_shadow_color . a = = 0 ) ) {
2020-10-02 14:02:02 +02:00
gloff . x + = glyphs [ i ] . advance ;
continue ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
// Get FX.
ItemFade * fade = nullptr ;
Item * fade_item = it ;
while ( fade_item ) {
if ( fade_item - > type = = ITEM_FADE ) {
fade = static_cast < ItemFade * > ( fade_item ) ;
break ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
fade_item = fade_item - > parent ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Vector < ItemFX * > fx_stack ;
_fetch_item_fx_stack ( it , fx_stack ) ;
bool custom_fx_ok = true ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Point2 fx_offset = Vector2 ( glyphs [ i ] . x_off , glyphs [ i ] . y_off ) ;
RID frid = glyphs [ i ] . font_rid ;
uint32_t gl = glyphs [ i ] . index ;
2021-09-21 11:27:06 +02:00
uint16_t gl_fl = glyphs [ i ] . flags ;
uint8_t gl_cn = glyphs [ i ] . count ;
bool cprev = false ;
if ( gl_cn = = 0 ) { // Parts of the same cluster, always connected.
cprev = true ;
}
if ( gl_fl & TextServer : : GRAPHEME_IS_RTL ) { // Check if previous grapheme cluster is connected.
if ( i > 0 & & ( glyphs [ i - 1 ] . flags & TextServer : : GRAPHEME_IS_CONNECTED ) ) {
cprev = true ;
}
} else {
if ( glyphs [ i ] . flags & TextServer : : GRAPHEME_IS_CONNECTED ) {
cprev = true ;
}
}
2020-10-02 14:02:02 +02:00
//Apply fx.
if ( fade ) {
2022-04-05 12:40:26 +02:00
float faded_visibility = 1.0f ;
2020-10-02 14:02:02 +02:00
if ( glyphs [ i ] . start > = fade - > starting_index ) {
faded_visibility - = ( float ) ( glyphs [ i ] . start - fade - > starting_index ) / ( float ) fade - > length ;
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility ;
2020-05-14 16:41:43 +02:00
}
2022-03-13 12:57:56 +01:00
font_outline_color . a = faded_visibility ;
2021-11-05 23:28:48 +01:00
font_shadow_color . a = faded_visibility ;
2020-10-02 14:02:02 +02:00
}
2014-02-10 02:10:30 +01:00
2022-03-13 12:57:56 +01:00
bool visible = ( font_outline_color . a ! = 0 ) | | ( font_shadow_color . a ! = 0 ) ;
2020-10-02 14:02:02 +02:00
for ( int j = 0 ; j < fx_stack . size ( ) ; j + + ) {
ItemFX * item_fx = fx_stack [ j ] ;
if ( item_fx - > type = = ITEM_CUSTOMFX & & custom_fx_ok ) {
ItemCustomFX * item_custom = static_cast < ItemCustomFX * > ( item_fx ) ;
Ref < CharFXTransform > charfx = item_custom - > char_fx_transform ;
Ref < RichTextEffect > custom_effect = item_custom - > custom_effect ;
if ( ! custom_effect . is_null ( ) ) {
charfx - > elapsed_time = item_custom - > elapsed_time ;
charfx - > range = Vector2i ( l . char_offset + glyphs [ i ] . start , l . char_offset + glyphs [ i ] . end ) ;
charfx - > visibility = visible ;
charfx - > outline = true ;
charfx - > font = frid ;
2020-12-27 14:30:33 +01:00
charfx - > glyph_index = gl ;
2021-09-21 11:27:06 +02:00
charfx - > glyph_flags = gl_fl ;
charfx - > glyph_count = gl_cn ;
2020-10-02 14:02:02 +02:00
charfx - > offset = fx_offset ;
charfx - > color = font_color ;
bool effect_status = custom_effect - > _process_effect_impl ( charfx ) ;
custom_fx_ok = effect_status ;
fx_offset + = charfx - > offset ;
font_color = charfx - > color ;
frid = charfx - > font ;
2020-12-27 14:30:33 +01:00
gl = charfx - > glyph_index ;
2020-10-02 14:02:02 +02:00
visible & = charfx - > visibility ;
}
} else if ( item_fx - > type = = ITEM_SHAKE ) {
ItemShake * item_shake = static_cast < ItemShake * > ( item_fx ) ;
2021-09-21 11:27:06 +02:00
if ( ! cprev ) {
uint64_t char_current_rand = item_shake - > offset_random ( glyphs [ i ] . start ) ;
uint64_t char_previous_rand = item_shake - > offset_previous_random ( glyphs [ i ] . start ) ;
uint64_t max_rand = 2147483647 ;
2022-09-05 14:05:50 +02:00
double current_offset = Math : : remap ( char_current_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
double previous_offset = Math : : remap ( char_previous_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
2021-09-21 11:27:06 +02:00
double n_time = ( double ) ( item_shake - > elapsed_time / ( 0.5f / item_shake - > rate ) ) ;
n_time = ( n_time > 1.0 ) ? 1.0 : n_time ;
item_shake - > prev_off = Point2 ( Math : : lerp ( Math : : sin ( previous_offset ) , Math : : sin ( current_offset ) , n_time ) , Math : : lerp ( Math : : cos ( previous_offset ) , Math : : cos ( current_offset ) , n_time ) ) * ( float ) item_shake - > strength / 10.0f ;
}
fx_offset + = item_shake - > prev_off ;
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_WAVE ) {
ItemWave * item_wave = static_cast < ItemWave * > ( item_fx ) ;
2021-09-21 11:27:06 +02:00
if ( ! cprev ) {
double value = Math : : sin ( item_wave - > frequency * item_wave - > elapsed_time + ( ( p_ofs . x + gloff . x ) / 50 ) ) * ( item_wave - > amplitude / 10.0f ) ;
item_wave - > prev_off = Point2 ( 0 , 1 ) * value ;
}
fx_offset + = item_wave - > prev_off ;
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_TORNADO ) {
ItemTornado * item_tornado = static_cast < ItemTornado * > ( item_fx ) ;
2021-09-21 11:27:06 +02:00
if ( ! cprev ) {
double torn_x = Math : : sin ( item_tornado - > frequency * item_tornado - > elapsed_time + ( ( p_ofs . x + gloff . x ) / 50 ) ) * ( item_tornado - > radius ) ;
double torn_y = Math : : cos ( item_tornado - > frequency * item_tornado - > elapsed_time + ( ( p_ofs . x + gloff . x ) / 50 ) ) * ( item_tornado - > radius ) ;
item_tornado - > prev_off = Point2 ( torn_x , torn_y ) ;
}
fx_offset + = item_tornado - > prev_off ;
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_RAINBOW ) {
ItemRainbow * item_rainbow = static_cast < ItemRainbow * > ( item_fx ) ;
font_color = font_color . from_hsv ( item_rainbow - > frequency * ( item_rainbow - > elapsed_time + ( ( p_ofs . x + gloff . x ) / 50 ) ) , item_rainbow - > saturation , item_rainbow - > value , font_color . a ) ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
// Draw glyph outlines.
2022-03-13 12:57:56 +01:00
const Color modulated_outline_color = font_outline_color * Color ( 1 , 1 , 1 , font_color . a ) ;
const Color modulated_shadow_color = font_shadow_color * Color ( 1 , 1 , 1 , font_color . a ) ;
2020-10-02 14:02:02 +02:00
for ( int j = 0 ; j < glyphs [ i ] . repeat ; j + + ) {
2020-05-14 16:41:43 +02:00
if ( visible ) {
2021-12-05 13:28:32 +01:00
bool skip = ( trim_chars & & l . char_offset + glyphs [ i ] . end > visible_characters ) | | ( trim_glyphs_ltr & & ( processed_glyphs_ol > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs_ol < total_glyphs - visible_glyphs ) ) ;
if ( ! skip & & frid ! = RID ( ) ) {
2022-03-13 12:57:56 +01:00
if ( modulated_shadow_color . a > 0 ) {
TS - > font_draw_glyph ( frid , ci , glyphs [ i ] . font_size , p_ofs + fx_offset + gloff + p_shadow_ofs , gl , modulated_shadow_color ) ;
2021-11-05 23:28:48 +01:00
}
2022-03-13 12:57:56 +01:00
if ( modulated_shadow_color . a > 0 & & p_shadow_outline_size > 0 ) {
TS - > font_draw_glyph_outline ( frid , ci , glyphs [ i ] . font_size , p_shadow_outline_size , p_ofs + fx_offset + gloff + p_shadow_ofs , gl , modulated_shadow_color ) ;
2021-11-05 23:28:48 +01:00
}
2022-03-13 12:57:56 +01:00
if ( modulated_outline_color . a ! = 0.0 & & size > 0 ) {
TS - > font_draw_glyph_outline ( frid , ci , glyphs [ i ] . font_size , size , p_ofs + fx_offset + gloff , gl , modulated_outline_color ) ;
2020-10-02 14:02:02 +02:00
}
}
2021-12-05 13:28:32 +01:00
processed_glyphs_ol + + ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
gloff . x + = glyphs [ i ] . advance ;
}
}
2020-01-26 23:11:09 +01:00
Vector2 fbg_line_off = off + p_ofs ;
// Draw background color box
Vector2i chr_range = TS - > shaped_text_get_range ( rid ) ;
_draw_fbg_boxes ( ci , rid , fbg_line_off , it_from , it_to , chr_range . x , chr_range . y , 0 ) ;
2020-10-02 14:02:02 +02:00
// Draw main text.
2022-09-01 17:52:49 +02:00
Color selection_fg = theme_cache . font_selected_color ;
Color selection_bg = theme_cache . selection_color ;
2020-10-02 14:02:02 +02:00
int sel_start = - 1 ;
int sel_end = - 1 ;
if ( selection . active & & ( selection . from_frame - > lines [ selection . from_line ] . char_offset + selection . from_char ) < = ( l . char_offset + TS - > shaped_text_get_range ( rid ) . y ) & & ( selection . to_frame - > lines [ selection . to_line ] . char_offset + selection . to_char ) > = ( l . char_offset + TS - > shaped_text_get_range ( rid ) . x ) ) {
sel_start = MAX ( TS - > shaped_text_get_range ( rid ) . x , ( selection . from_frame - > lines [ selection . from_line ] . char_offset + selection . from_char ) - l . char_offset ) ;
sel_end = MIN ( TS - > shaped_text_get_range ( rid ) . y , ( selection . to_frame - > lines [ selection . to_line ] . char_offset + selection . to_char ) - l . char_offset ) ;
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( rid , sel_start , sel_end ) ;
for ( int i = 0 ; i < sel . size ( ) ; i + + ) {
Rect2 rect = Rect2 ( sel [ i ] . x + p_ofs . x + off . x , p_ofs . y + off . y - TS - > shaped_text_get_ascent ( rid ) , sel [ i ] . y - sel [ i ] . x , TS - > shaped_text_get_size ( rid ) . y ) ;
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , rect , selection_bg ) ;
}
}
2022-02-21 18:34:16 +01:00
Vector2 ul_start ;
bool ul_started = false ;
Color ul_color ;
Vector2 dot_ul_start ;
bool dot_ul_started = false ;
Color dot_ul_color ;
Vector2 st_start ;
bool st_started = false ;
Color st_color ;
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < gl_size ; i + + ) {
bool selected = selection . active & & ( sel_start ! = - 1 ) & & ( glyphs [ i ] . start > = sel_start ) & & ( glyphs [ i ] . end < = sel_end ) ;
Item * it = _get_item_at_pos ( it_from , it_to , glyphs [ i ] . start ) ;
Color font_color = _find_color ( it , p_base_color ) ;
2022-02-21 18:34:16 +01:00
if ( _find_underline ( it ) | | ( _find_meta ( it , nullptr ) & & underline_meta ) ) {
if ( ! ul_started ) {
ul_started = true ;
ul_start = p_ofs + Vector2 ( off . x , off . y ) ;
ul_color = font_color ;
ul_color . a * = 0.5 ;
}
} else if ( ul_started ) {
ul_started = false ;
2020-10-02 14:02:02 +02:00
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-02-21 18:34:16 +01:00
draw_line ( ul_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , ul_color , underline_width ) ;
}
if ( _find_hint ( it , nullptr ) & & underline_hint ) {
if ( ! dot_ul_started ) {
dot_ul_started = true ;
dot_ul_start = p_ofs + Vector2 ( off . x , off . y ) ;
dot_ul_color = font_color ;
dot_ul_color . a * = 0.5 ;
}
} else if ( dot_ul_started ) {
dot_ul_started = false ;
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-02-21 18:34:16 +01:00
draw_dashed_line ( dot_ul_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , dot_ul_color , underline_width , underline_width * 2 ) ;
2022-01-11 05:54:04 +01:00
}
if ( _find_strikethrough ( it ) ) {
2022-02-21 18:34:16 +01:00
if ( ! st_started ) {
st_started = true ;
st_start = p_ofs + Vector2 ( off . x , off . y ) ;
st_color = font_color ;
st_color . a * = 0.5 ;
}
} else if ( st_started ) {
st_started = false ;
2020-10-02 14:02:02 +02:00
float y_off = - TS - > shaped_text_get_ascent ( rid ) + TS - > shaped_text_get_size ( rid ) . y / 2 ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-02-21 18:34:16 +01:00
draw_line ( st_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , st_color , underline_width ) ;
2020-10-02 14:02:02 +02:00
}
2015-06-02 00:42:34 +02:00
2020-10-02 14:02:02 +02:00
// Get FX.
ItemFade * fade = nullptr ;
Item * fade_item = it ;
while ( fade_item ) {
if ( fade_item - > type = = ITEM_FADE ) {
fade = static_cast < ItemFade * > ( fade_item ) ;
break ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
fade_item = fade_item - > parent ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Vector < ItemFX * > fx_stack ;
_fetch_item_fx_stack ( it , fx_stack ) ;
bool custom_fx_ok = true ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Point2 fx_offset = Vector2 ( glyphs [ i ] . x_off , glyphs [ i ] . y_off ) ;
RID frid = glyphs [ i ] . font_rid ;
uint32_t gl = glyphs [ i ] . index ;
2021-09-21 11:27:06 +02:00
uint16_t gl_fl = glyphs [ i ] . flags ;
uint8_t gl_cn = glyphs [ i ] . count ;
bool cprev = false ;
if ( gl_cn = = 0 ) { // Parts of the same grapheme cluster, always connected.
cprev = true ;
}
if ( gl_fl & TextServer : : GRAPHEME_IS_RTL ) { // Check if previous grapheme cluster is connected.
if ( i > 0 & & ( glyphs [ i - 1 ] . flags & TextServer : : GRAPHEME_IS_CONNECTED ) ) {
cprev = true ;
}
} else {
if ( glyphs [ i ] . flags & TextServer : : GRAPHEME_IS_CONNECTED ) {
cprev = true ;
}
}
2020-01-31 06:05:52 +01:00
2020-10-02 14:02:02 +02:00
//Apply fx.
if ( fade ) {
2022-04-05 12:40:26 +02:00
float faded_visibility = 1.0f ;
2020-10-02 14:02:02 +02:00
if ( glyphs [ i ] . start > = fade - > starting_index ) {
faded_visibility - = ( float ) ( glyphs [ i ] . start - fade - > starting_index ) / ( float ) fade - > length ;
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility ;
2016-10-17 21:03:47 +02:00
}
2020-10-02 14:02:02 +02:00
font_color . a = faded_visibility ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
bool visible = ( font_color . a ! = 0 ) ;
for ( int j = 0 ; j < fx_stack . size ( ) ; j + + ) {
ItemFX * item_fx = fx_stack [ j ] ;
if ( item_fx - > type = = ITEM_CUSTOMFX & & custom_fx_ok ) {
ItemCustomFX * item_custom = static_cast < ItemCustomFX * > ( item_fx ) ;
Ref < CharFXTransform > charfx = item_custom - > char_fx_transform ;
Ref < RichTextEffect > custom_effect = item_custom - > custom_effect ;
if ( ! custom_effect . is_null ( ) ) {
charfx - > elapsed_time = item_custom - > elapsed_time ;
charfx - > range = Vector2i ( l . char_offset + glyphs [ i ] . start , l . char_offset + glyphs [ i ] . end ) ;
charfx - > visibility = visible ;
charfx - > outline = false ;
charfx - > font = frid ;
2020-12-27 14:30:33 +01:00
charfx - > glyph_index = gl ;
2021-09-21 11:27:06 +02:00
charfx - > glyph_flags = gl_fl ;
charfx - > glyph_count = gl_cn ;
2020-10-02 14:02:02 +02:00
charfx - > offset = fx_offset ;
charfx - > color = font_color ;
bool effect_status = custom_effect - > _process_effect_impl ( charfx ) ;
custom_fx_ok = effect_status ;
fx_offset + = charfx - > offset ;
font_color = charfx - > color ;
frid = charfx - > font ;
2020-12-27 14:30:33 +01:00
gl = charfx - > glyph_index ;
2020-10-02 14:02:02 +02:00
visible & = charfx - > visibility ;
2015-12-26 14:25:17 +01:00
}
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_SHAKE ) {
ItemShake * item_shake = static_cast < ItemShake * > ( item_fx ) ;
2021-09-21 11:27:06 +02:00
if ( ! cprev ) {
uint64_t char_current_rand = item_shake - > offset_random ( glyphs [ i ] . start ) ;
uint64_t char_previous_rand = item_shake - > offset_previous_random ( glyphs [ i ] . start ) ;
uint64_t max_rand = 2147483647 ;
2022-09-05 14:05:50 +02:00
double current_offset = Math : : remap ( char_current_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
double previous_offset = Math : : remap ( char_previous_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
2021-09-21 11:27:06 +02:00
double n_time = ( double ) ( item_shake - > elapsed_time / ( 0.5f / item_shake - > rate ) ) ;
n_time = ( n_time > 1.0 ) ? 1.0 : n_time ;
item_shake - > prev_off = Point2 ( Math : : lerp ( Math : : sin ( previous_offset ) , Math : : sin ( current_offset ) , n_time ) , Math : : lerp ( Math : : cos ( previous_offset ) , Math : : cos ( current_offset ) , n_time ) ) * ( float ) item_shake - > strength / 10.0f ;
}
fx_offset + = item_shake - > prev_off ;
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_WAVE ) {
ItemWave * item_wave = static_cast < ItemWave * > ( item_fx ) ;
2021-09-21 11:27:06 +02:00
if ( ! cprev ) {
double value = Math : : sin ( item_wave - > frequency * item_wave - > elapsed_time + ( ( p_ofs . x + off . x ) / 50 ) ) * ( item_wave - > amplitude / 10.0f ) ;
item_wave - > prev_off = Point2 ( 0 , 1 ) * value ;
}
fx_offset + = item_wave - > prev_off ;
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_TORNADO ) {
ItemTornado * item_tornado = static_cast < ItemTornado * > ( item_fx ) ;
2021-09-21 11:27:06 +02:00
if ( ! cprev ) {
double torn_x = Math : : sin ( item_tornado - > frequency * item_tornado - > elapsed_time + ( ( p_ofs . x + off . x ) / 50 ) ) * ( item_tornado - > radius ) ;
double torn_y = Math : : cos ( item_tornado - > frequency * item_tornado - > elapsed_time + ( ( p_ofs . x + off . x ) / 50 ) ) * ( item_tornado - > radius ) ;
item_tornado - > prev_off = Point2 ( torn_x , torn_y ) ;
}
fx_offset + = item_tornado - > prev_off ;
2020-10-02 14:02:02 +02:00
} else if ( item_fx - > type = = ITEM_RAINBOW ) {
ItemRainbow * item_rainbow = static_cast < ItemRainbow * > ( item_fx ) ;
font_color = font_color . from_hsv ( item_rainbow - > frequency * ( item_rainbow - > elapsed_time + ( ( p_ofs . x + off . x ) / 50 ) ) , item_rainbow - > saturation , item_rainbow - > value , font_color . a ) ;
}
}
2018-01-04 08:07:56 +01:00
2020-10-02 14:02:02 +02:00
if ( selected ) {
font_color = override_selected_font_color ? selection_fg : font_color ;
}
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
// Draw glyphs.
for ( int j = 0 ; j < glyphs [ i ] . repeat ; j + + ) {
2022-06-13 09:53:55 +02:00
bool skip = ( trim_chars & & l . char_offset + glyphs [ i ] . end > visible_characters ) | | ( trim_glyphs_ltr & & ( r_processed_glyphs > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( r_processed_glyphs < total_glyphs - visible_glyphs ) ) ;
2020-10-02 14:02:02 +02:00
if ( visible ) {
2021-12-05 13:28:32 +01:00
if ( ! skip ) {
if ( frid ! = RID ( ) ) {
TS - > font_draw_glyph ( frid , ci , glyphs [ i ] . font_size , p_ofs + fx_offset + off , gl , selected ? selection_fg : font_color ) ;
} else if ( ( glyphs [ i ] . flags & TextServer : : GRAPHEME_IS_VIRTUAL ) ! = TextServer : : GRAPHEME_IS_VIRTUAL ) {
TS - > draw_hex_code_box ( ci , glyphs [ i ] . font_size , p_ofs + fx_offset + off , gl , selected ? selection_fg : font_color ) ;
}
2020-10-02 14:02:02 +02:00
}
2021-12-05 13:28:32 +01:00
r_processed_glyphs + + ;
2020-10-02 14:02:02 +02:00
}
2022-06-13 09:53:55 +02:00
if ( skip ) {
// End underline/overline/strikethrough is previous glyph is skipped.
if ( ul_started ) {
ul_started = false ;
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-06-13 09:53:55 +02:00
draw_line ( ul_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , ul_color , underline_width ) ;
}
if ( dot_ul_started ) {
dot_ul_started = false ;
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-06-13 09:53:55 +02:00
draw_dashed_line ( dot_ul_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , dot_ul_color , underline_width , underline_width * 2 ) ;
}
if ( st_started ) {
st_started = false ;
float y_off = - TS - > shaped_text_get_ascent ( rid ) + TS - > shaped_text_get_size ( rid ) . y / 2 ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-06-13 09:53:55 +02:00
draw_line ( st_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , st_color , underline_width ) ;
}
}
2020-10-02 14:02:02 +02:00
off . x + = glyphs [ i ] . advance ;
}
}
2022-02-21 18:34:16 +01:00
if ( ul_started ) {
ul_started = false ;
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-02-21 18:34:16 +01:00
draw_line ( ul_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , ul_color , underline_width ) ;
}
if ( dot_ul_started ) {
dot_ul_started = false ;
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-02-21 18:34:16 +01:00
draw_dashed_line ( dot_ul_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , dot_ul_color , underline_width , underline_width * 2 ) ;
}
if ( st_started ) {
st_started = false ;
float y_off = - TS - > shaped_text_get_ascent ( rid ) + TS - > shaped_text_get_size ( rid ) . y / 2 ;
2022-09-01 17:52:49 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * theme_cache . base_scale ;
2022-02-21 18:34:16 +01:00
draw_line ( st_start + Vector2 ( 0 , y_off ) , p_ofs + Vector2 ( off . x , off . y + y_off ) , st_color , underline_width ) ;
}
2020-01-26 23:11:09 +01:00
// Draw foreground color box
_draw_fbg_boxes ( ci , rid , fbg_line_off , it_from , it_to , chr_range . x , chr_range . y , 1 ) ;
2022-05-09 11:47:10 +02:00
off . y + = TS - > shaped_text_get_descent ( rid ) ;
2020-10-02 14:02:02 +02:00
}
2021-01-12 13:03:10 +01:00
return line_count ;
2020-10-02 14:02:02 +02:00
}
2015-12-26 14:25:17 +01:00
2022-08-23 08:57:22 +02:00
void RichTextLabel : : _find_click ( ItemFrame * p_frame , const Point2i & p_click , ItemFrame * * r_click_frame , int * r_click_line , Item * * r_click_item , int * r_click_char , bool * r_outside , bool p_meta ) {
2020-10-02 14:02:02 +02:00
if ( r_click_item ) {
* r_click_item = nullptr ;
}
if ( r_click_char ! = nullptr ) {
* r_click_char = 0 ;
}
if ( r_outside ! = nullptr ) {
* r_outside = true ;
}
Size2 size = get_size ( ) ;
Rect2 text_rect = _get_text_rect ( ) ;
int vofs = vscroll - > get_value ( ) ;
// Search for the first line.
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
int from_line = _find_first_line ( 0 , to_line , vofs ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
Point2 ofs = text_rect . get_position ( ) + Vector2 ( 0 , main - > lines [ from_line ] . offset . y - vofs ) ;
2022-05-18 09:17:55 +02:00
while ( ofs . y < size . height & & from_line < to_line ) {
MutexLock lock ( main - > lines [ from_line ] . text_buf - > get_mutex ( ) ) ;
2022-08-23 08:57:22 +02:00
_find_click_in_line ( p_frame , from_line , ofs , text_rect . size . x , p_click , r_click_frame , r_click_line , r_click_item , r_click_char , false , p_meta ) ;
2022-09-01 17:52:49 +02:00
ofs . y + = main - > lines [ from_line ] . text_buf - > get_size ( ) . y + main - > lines [ from_line ] . text_buf - > get_line_count ( ) * theme_cache . line_separation ;
2020-10-02 14:02:02 +02:00
if ( ( ( r_click_item ! = nullptr ) & & ( ( * r_click_item ) ! = nullptr ) ) | | ( ( r_click_frame ! = nullptr ) & & ( ( * r_click_frame ) ! = nullptr ) ) ) {
if ( r_outside ! = nullptr ) {
* r_outside = false ;
}
return ;
}
from_line + + ;
}
}
2015-12-26 14:25:17 +01:00
2022-08-23 08:57:22 +02:00
float RichTextLabel : : _find_click_in_line ( ItemFrame * p_frame , int p_line , const Vector2 & p_ofs , int p_width , const Point2i & p_click , ItemFrame * * r_click_frame , int * r_click_line , Item * * r_click_item , int * r_click_char , bool p_table , bool p_meta ) {
2020-10-02 14:02:02 +02:00
Vector2 off ;
2015-12-26 14:25:17 +01:00
2022-07-06 14:16:50 +02:00
bool line_clicked = false ;
float text_rect_begin = 0.0 ;
2020-10-02 14:02:02 +02:00
int char_pos = - 1 ;
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
MutexLock lock ( l . text_buf - > get_mutex ( ) ) ;
2020-10-02 14:02:02 +02:00
bool rtl = ( l . text_buf - > get_direction ( ) = = TextServer : : DIRECTION_RTL ) ;
bool lrtl = is_layout_rtl ( ) ;
2022-02-09 17:27:39 +01:00
// Table hit test results.
2020-10-02 14:02:02 +02:00
bool table_hit = false ;
2022-02-09 17:27:39 +01:00
Vector2i table_range ;
float table_offy = 0.f ;
ItemFrame * table_click_frame = nullptr ;
int table_click_line = - 1 ;
Item * table_click_item = nullptr ;
int table_click_char = - 1 ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
for ( int line = 0 ; line < l . text_buf - > get_line_count ( ) ; line + + ) {
RID rid = l . text_buf - > get_line_rid ( line ) ;
2018-03-24 18:09:35 +01:00
2020-10-02 14:02:02 +02:00
float width = l . text_buf - > get_width ( ) ;
float length = TS - > shaped_text_get_width ( rid ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
if ( rtl ) {
off . x = p_width - l . offset . x - width ;
if ( ! lrtl & & p_frame = = main ) { // Skip Scrollbar.
off . x - = scroll_w ;
}
} else {
off . x = l . offset . x ;
if ( lrtl & & p_frame = = main ) { // Skip Scrollbar.
off . x + = scroll_w ;
}
}
2015-12-26 14:25:17 +01:00
2021-11-25 03:58:47 +01:00
switch ( l . text_buf - > get_alignment ( ) ) {
case HORIZONTAL_ALIGNMENT_FILL :
case HORIZONTAL_ALIGNMENT_LEFT : {
2020-10-02 14:02:02 +02:00
if ( rtl ) {
off . x + = width - length ;
}
} break ;
2021-11-25 03:58:47 +01:00
case HORIZONTAL_ALIGNMENT_CENTER : {
2020-10-02 14:02:02 +02:00
off . x + = Math : : floor ( ( width - length ) / 2.0 ) ;
} break ;
2021-11-25 03:58:47 +01:00
case HORIZONTAL_ALIGNMENT_RIGHT : {
2020-10-02 14:02:02 +02:00
if ( ! rtl ) {
off . x + = width - length ;
2015-12-26 14:25:17 +01:00
}
2020-10-02 14:02:02 +02:00
} break ;
}
2015-12-26 14:25:17 +01:00
2022-05-09 11:47:10 +02:00
off . y + = TS - > shaped_text_get_ascent ( rid ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
Array objects = TS - > shaped_text_get_objects ( rid ) ;
for ( int i = 0 ; i < objects . size ( ) ; i + + ) {
2022-04-05 12:40:26 +02:00
Item * it = reinterpret_cast < Item * > ( ( uint64_t ) objects [ i ] ) ;
2020-10-02 14:02:02 +02:00
if ( it ! = nullptr ) {
Rect2 rect = TS - > shaped_text_get_object_rect ( rid , objects [ i ] ) ;
2022-02-09 17:27:39 +01:00
rect . position + = p_ofs + off ;
if ( p_click . y > = rect . position . y & & p_click . y < = rect . position . y + rect . size . y ) {
2020-10-02 14:02:02 +02:00
switch ( it - > type ) {
case ITEM_TABLE : {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
int idx = 0 ;
int col_count = table - > columns . size ( ) ;
int row_count = table - > rows . size ( ) ;
2015-12-26 14:25:17 +01:00
2021-07-16 05:45:57 +02:00
for ( Item * E : table - > subitems ) {
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
int col = idx % col_count ;
int row = idx / col_count ;
if ( frame - > lines . size ( ) ! = 0 & & row < row_count ) {
Vector2 coff = frame - > lines [ 0 ] . offset ;
if ( rtl ) {
coff . x = rect . size . width - table - > columns [ col ] . width - coff . x ;
}
2022-09-01 17:52:49 +02:00
Rect2 crect = Rect2 ( rect . position + coff - frame - > padding . position , Size2 ( table - > columns [ col ] . width + theme_cache . table_h_separation , table - > rows [ row ] + theme_cache . table_v_separation ) + frame - > padding . position + frame - > padding . size ) ;
2020-10-02 14:02:02 +02:00
if ( col = = col_count - 1 ) {
if ( rtl ) {
crect . size . x = crect . position . x + crect . size . x ;
crect . position . x = 0 ;
} else {
crect . size . x = get_size ( ) . x ;
}
}
if ( crect . has_point ( p_click ) ) {
2022-05-18 09:17:55 +02:00
for ( int j = 0 ; j < ( int ) frame - > lines . size ( ) ; j + + ) {
2022-08-23 08:57:22 +02:00
_find_click_in_line ( frame , j , rect . position + Vector2 ( 0 , frame - > lines [ j ] . offset . y ) , rect . size . x , p_click , & table_click_frame , & table_click_line , & table_click_item , & table_click_char , true , p_meta ) ;
2022-02-09 17:27:39 +01:00
if ( table_click_frame & & table_click_item ) {
// Save cell detected cell hit data.
table_range = Vector2i ( INT32_MAX , 0 ) ;
for ( Item * F : table - > subitems ) {
ItemFrame * sub_frame = static_cast < ItemFrame * > ( F ) ;
2022-05-18 09:17:55 +02:00
for ( int k = 0 ; k < ( int ) sub_frame - > lines . size ( ) ; k + + ) {
2022-02-09 17:27:39 +01:00
table_range . x = MIN ( table_range . x , sub_frame - > lines [ k ] . char_offset ) ;
table_range . y = MAX ( table_range . y , sub_frame - > lines [ k ] . char_offset + sub_frame - > lines [ k ] . char_count ) ;
}
}
table_offy = off . y ;
table_hit = true ;
2020-10-02 14:02:02 +02:00
}
}
}
2017-11-05 11:38:14 +01:00
}
2020-10-02 14:02:02 +02:00
idx + + ;
2015-12-26 19:18:01 +01:00
}
2020-10-02 14:02:02 +02:00
} break ;
default :
break ;
2015-12-26 14:25:17 +01:00
}
}
2019-04-09 17:08:36 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-02-09 17:27:39 +01:00
Rect2 rect = Rect2 ( p_ofs + off - Vector2 ( 0 , TS - > shaped_text_get_ascent ( rid ) ) - p_frame - > padding . position , TS - > shaped_text_get_size ( rid ) + p_frame - > padding . position + p_frame - > padding . size ) ;
if ( p_table ) {
2022-09-01 17:52:49 +02:00
rect . size . y + = theme_cache . table_v_separation ;
2022-02-09 17:27:39 +01:00
}
2020-10-02 14:02:02 +02:00
2022-02-09 17:27:39 +01:00
if ( p_click . y > = rect . position . y & & p_click . y < = rect . position . y + rect . size . y ) {
2022-07-06 14:16:50 +02:00
if ( ( ! rtl & & p_click . x > = rect . position . x ) | | ( rtl & & p_click . x < = rect . position . x + rect . size . x ) ) {
2022-08-23 08:57:22 +02:00
if ( p_meta ) {
int64_t glyph_idx = TS - > shaped_text_hit_test_grapheme ( rid , p_click . x - rect . position . x ) ;
if ( glyph_idx > = 0 ) {
const Glyph * glyphs = TS - > shaped_text_get_glyphs ( rid ) ;
char_pos = glyphs [ glyph_idx ] . start ;
}
} else {
char_pos = TS - > shaped_text_hit_test_position ( rid , p_click . x - rect . position . x ) ;
}
2022-07-06 14:16:50 +02:00
}
line_clicked = true ;
text_rect_begin = rtl ? rect . position . x + rect . size . x : rect . position . x ;
2020-10-02 14:02:02 +02:00
}
2022-02-09 17:27:39 +01:00
// If table hit was detected, and line hit is in the table bounds use table hit.
if ( table_hit & & ( ( ( char_pos + p_frame - > lines [ p_line ] . char_offset ) > = table_range . x & & ( char_pos + p_frame - > lines [ p_line ] . char_offset ) < = table_range . y ) | | char_pos = = - 1 ) ) {
if ( r_click_frame ! = nullptr ) {
* r_click_frame = table_click_frame ;
}
if ( r_click_line ! = nullptr ) {
* r_click_line = table_click_line ;
}
if ( r_click_item ! = nullptr ) {
* r_click_item = table_click_item ;
}
if ( r_click_char ! = nullptr ) {
* r_click_char = table_click_char ;
}
return table_offy ;
}
2022-09-01 17:52:49 +02:00
off . y + = TS - > shaped_text_get_descent ( rid ) + theme_cache . line_separation ;
2020-10-02 14:02:02 +02:00
}
2014-02-10 02:10:30 +01:00
2022-02-09 17:27:39 +01:00
// Text line hit.
2022-07-06 14:16:50 +02:00
if ( line_clicked ) {
2020-10-02 14:02:02 +02:00
// Find item.
if ( r_click_item ! = nullptr ) {
Item * it = p_frame - > lines [ p_line ] . from ;
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2022-07-06 14:16:50 +02:00
if ( char_pos > = 0 ) {
* r_click_item = _get_item_at_pos ( it , it_to , char_pos ) ;
} else {
int stop = text_rect_begin ;
* r_click_item = _find_indentable ( it ) ;
while ( * r_click_item ) {
2022-09-01 17:52:49 +02:00
Ref < Font > font = theme_cache . normal_font ;
int font_size = theme_cache . normal_font_size ;
2022-07-26 09:45:40 +02:00
ItemFont * font_it = _find_font ( * r_click_item ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2022-07-06 14:16:50 +02:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( * r_click_item ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2021-03-14 18:54:48 +01:00
}
2022-07-06 14:16:50 +02:00
if ( rtl ) {
stop + = tab_size * font - > get_char_size ( ' ' , font_size ) . width ;
if ( stop > p_click . x ) {
break ;
}
} else {
stop - = tab_size * font - > get_char_size ( ' ' , font_size ) . width ;
if ( stop < p_click . x ) {
break ;
}
}
* r_click_item = _find_indentable ( ( * r_click_item ) - > parent ) ;
2021-03-14 18:54:48 +01:00
}
}
2020-10-02 14:02:02 +02:00
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( r_click_frame ! = nullptr ) {
* r_click_frame = p_frame ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( r_click_line ! = nullptr ) {
* r_click_line = p_line ;
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( r_click_char ! = nullptr ) {
* r_click_char = char_pos ;
2014-02-10 02:10:30 +01:00
}
}
2016-10-17 21:03:47 +02:00
2020-10-02 14:02:02 +02:00
return off . y ;
2014-02-10 02:10:30 +01:00
}
void RichTextLabel : : _scroll_changed ( double ) {
2020-02-19 20:12:07 +01:00
if ( updating_scroll ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( scroll_follow & & vscroll - > get_value ( ) > = ( vscroll - > get_max ( ) - vscroll - > get_page ( ) ) ) {
2017-03-05 16:44:50 +01:00
scroll_following = true ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
scroll_following = false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2019-04-27 21:15:07 +02:00
scroll_updated = true ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
2021-02-02 03:16:37 +01:00
void RichTextLabel : : _update_fx ( RichTextLabel : : ItemFrame * p_frame , double p_delta_time ) {
2018-11-10 03:44:58 +01:00
Item * it = p_frame ;
while ( it ) {
2020-04-02 01:20:12 +02:00
ItemFX * ifx = nullptr ;
2019-09-25 22:05:42 +02:00
if ( it - > type = = ITEM_CUSTOMFX | | it - > type = = ITEM_SHAKE | | it - > type = = ITEM_WAVE | | it - > type = = ITEM_TORNADO | | it - > type = = ITEM_RAINBOW ) {
ifx = static_cast < ItemFX * > ( it ) ;
}
2018-11-10 03:44:58 +01:00
if ( ! ifx ) {
it = _get_next_item ( it , true ) ;
continue ;
}
ifx - > elapsed_time + = p_delta_time ;
2020-04-02 01:20:12 +02:00
ItemShake * shake = nullptr ;
2019-09-25 22:05:42 +02:00
if ( it - > type = = ITEM_SHAKE ) {
shake = static_cast < ItemShake * > ( it ) ;
}
2018-11-10 03:44:58 +01:00
if ( shake ) {
bool cycle = ( shake - > elapsed_time > ( 1.0f / shake - > rate ) ) ;
if ( cycle ) {
shake - > elapsed_time - = ( 1.0f / shake - > rate ) ;
shake - > reroll_random ( ) ;
}
}
it = _get_next_item ( it , true ) ;
}
}
2022-05-18 09:17:55 +02:00
int RichTextLabel : : _find_first_line ( int p_from , int p_to , int p_vofs ) const {
int l = p_from ;
int r = p_to ;
while ( l < r ) {
int m = Math : : floor ( double ( l + r ) / 2.0 ) ;
MutexLock lock ( main - > lines [ m ] . text_buf - > get_mutex ( ) ) ;
2022-06-05 12:58:04 +02:00
int ofs = _calculate_line_vertical_offset ( main - > lines [ m ] ) ;
2022-05-18 09:17:55 +02:00
if ( ofs < p_vofs ) {
l = m + 1 ;
} else {
r = m ;
}
}
return l ;
}
2022-06-05 12:58:04 +02:00
_FORCE_INLINE_ float RichTextLabel : : _calculate_line_vertical_offset ( const RichTextLabel : : Line & line ) const {
2022-09-01 17:52:49 +02:00
return line . get_height ( theme_cache . line_separation ) ;
}
void RichTextLabel : : _update_theme_item_cache ( ) {
Control : : _update_theme_item_cache ( ) ;
theme_cache . normal_style = get_theme_stylebox ( SNAME ( " normal " ) ) ;
theme_cache . focus_style = get_theme_stylebox ( SNAME ( " focus " ) ) ;
theme_cache . progress_bg_style = get_theme_stylebox ( SNAME ( " bg " ) , SNAME ( " ProgressBar " ) ) ;
theme_cache . progress_fg_style = get_theme_stylebox ( SNAME ( " fg " ) , SNAME ( " ProgressBar " ) ) ;
theme_cache . line_separation = get_theme_constant ( SNAME ( " line_separation " ) ) ;
theme_cache . normal_font = get_theme_font ( SNAME ( " normal_font " ) ) ;
theme_cache . normal_font_size = get_theme_font_size ( SNAME ( " normal_font_size " ) ) ;
theme_cache . default_color = get_theme_color ( SNAME ( " default_color " ) ) ;
theme_cache . font_selected_color = get_theme_color ( SNAME ( " font_selected_color " ) ) ;
theme_cache . selection_color = get_theme_color ( SNAME ( " selection_color " ) ) ;
theme_cache . font_outline_color = get_theme_color ( SNAME ( " font_outline_color " ) ) ;
theme_cache . font_shadow_color = get_theme_color ( SNAME ( " font_shadow_color " ) ) ;
theme_cache . shadow_outline_size = get_theme_constant ( SNAME ( " shadow_outline_size " ) ) ;
theme_cache . shadow_offset_x = get_theme_constant ( SNAME ( " shadow_offset_x " ) ) ;
theme_cache . shadow_offset_y = get_theme_constant ( SNAME ( " shadow_offset_y " ) ) ;
theme_cache . outline_size = get_theme_constant ( SNAME ( " outline_size " ) ) ;
theme_cache . outline_color = get_theme_color ( SNAME ( " outline_color " ) ) ;
theme_cache . bold_font = get_theme_font ( SNAME ( " bold_font " ) ) ;
theme_cache . bold_font_size = get_theme_font_size ( SNAME ( " bold_font_size " ) ) ;
theme_cache . bold_italics_font = get_theme_font ( SNAME ( " bold_italics_font " ) ) ;
theme_cache . bold_italics_font_size = get_theme_font_size ( SNAME ( " bold_italics_font_size " ) ) ;
theme_cache . italics_font = get_theme_font ( SNAME ( " italics_font " ) ) ;
theme_cache . italics_font_size = get_theme_font_size ( SNAME ( " italics_font_size " ) ) ;
theme_cache . mono_font = get_theme_font ( SNAME ( " mono_font " ) ) ;
theme_cache . mono_font_size = get_theme_font_size ( SNAME ( " mono_font_size " ) ) ;
theme_cache . table_h_separation = get_theme_constant ( SNAME ( " table_h_separation " ) ) ;
theme_cache . table_v_separation = get_theme_constant ( SNAME ( " table_v_separation " ) ) ;
theme_cache . table_odd_row_bg = get_theme_color ( SNAME ( " table_odd_row_bg " ) ) ;
theme_cache . table_even_row_bg = get_theme_color ( SNAME ( " table_even_row_bg " ) ) ;
theme_cache . table_border = get_theme_color ( SNAME ( " table_border " ) ) ;
theme_cache . base_scale = get_theme_default_base_scale ( ) ;
2022-06-05 12:58:04 +02:00
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : _notification ( int p_what ) {
switch ( p_what ) {
2020-01-08 14:01:08 +01:00
case NOTIFICATION_MOUSE_EXIT : {
if ( meta_hovering ) {
2020-04-02 01:20:12 +02:00
meta_hovering = nullptr ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " meta_hover_ended " ) , current_meta ) ;
2020-01-08 14:01:08 +01:00
current_meta = false ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-01-08 14:01:08 +01:00
}
} break ;
2022-02-15 18:06:48 +01:00
2014-02-10 02:10:30 +01:00
case NOTIFICATION_RESIZED : {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
main - > first_resized_line . store ( 0 ) ; //invalidate ALL
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2015-04-21 21:01:58 +02:00
} break ;
2022-02-15 18:06:48 +01:00
2022-01-20 08:30:42 +01:00
case NOTIFICATION_THEME_CHANGED : {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
main - > first_invalid_font_line . store ( 0 ) ; //invalidate ALL
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-01-20 08:30:42 +01:00
} break ;
2022-02-15 18:06:48 +01:00
2015-04-21 21:01:58 +02:00
case NOTIFICATION_ENTER_TREE : {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2021-12-09 10:42:46 +01:00
if ( ! text . is_empty ( ) ) {
2020-05-29 16:25:12 +02:00
set_text ( text ) ;
2020-05-14 16:41:43 +02:00
}
2016-07-17 18:21:44 +02:00
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2015-04-21 21:01:58 +02:00
} break ;
2022-02-15 18:06:48 +01:00
2022-06-06 22:45:01 +02:00
case NOTIFICATION_PREDELETE :
2022-05-18 09:17:55 +02:00
case NOTIFICATION_EXIT_TREE : {
_stop_thread ( ) ;
} break ;
2020-10-02 14:02:02 +02:00
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
case NOTIFICATION_TRANSLATION_CHANGED : {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
2022-02-15 18:06:48 +01:00
2022-05-18 09:17:55 +02:00
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-05-18 09:17:55 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
case NOTIFICATION_DRAW : {
2017-03-05 16:44:50 +01:00
RID ci = get_canvas_item ( ) ;
2014-02-10 02:10:30 +01:00
Size2 size = get_size ( ) ;
2017-09-02 18:20:04 +02:00
2022-09-01 17:52:49 +02:00
draw_style_box ( theme_cache . normal_style , Rect2 ( Point2 ( ) , size ) ) ;
2014-02-10 02:10:30 +01:00
2014-02-16 01:16:33 +01:00
if ( has_focus ( ) ) {
2020-03-27 19:21:27 +01:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_clip_ignore ( ci , true ) ;
2022-09-01 17:52:49 +02:00
draw_style_box ( theme_cache . focus_style , Rect2 ( Point2 ( ) , size ) ) ;
2020-03-27 19:21:27 +01:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_clip_ignore ( ci , false ) ;
2014-02-16 01:16:33 +01:00
}
2022-05-18 09:17:55 +02:00
// Start text shaping.
if ( _validate_line_caches ( ) ) {
set_physics_process_internal ( false ) ; // Disable auto refresh, if text is fully processed.
} else {
// Draw loading progress bar.
if ( ( progress_delay > 0 ) & & ( OS : : get_singleton ( ) - > get_ticks_msec ( ) - loading_started > = ( uint64_t ) progress_delay ) ) {
2022-09-01 17:52:49 +02:00
Vector2 p_size = Vector2 ( size . width - ( theme_cache . normal_style - > get_offset ( ) . x + vscroll - > get_combined_minimum_size ( ) . width ) * 2 , vscroll - > get_combined_minimum_size ( ) . width ) ;
Vector2 p_pos = Vector2 ( theme_cache . normal_style - > get_offset ( ) . x , size . height - theme_cache . normal_style - > get_offset ( ) . y - vscroll - > get_combined_minimum_size ( ) . width ) ;
2022-05-18 09:17:55 +02:00
2022-09-01 17:52:49 +02:00
draw_style_box ( theme_cache . progress_bg_style , Rect2 ( p_pos , p_size ) ) ;
2022-05-18 09:17:55 +02:00
bool right_to_left = is_layout_rtl ( ) ;
double r = loaded . load ( ) ;
2022-09-01 17:52:49 +02:00
int mp = theme_cache . progress_fg_style - > get_minimum_size ( ) . width ;
2022-05-18 09:17:55 +02:00
int p = round ( r * ( p_size . width - mp ) ) ;
if ( right_to_left ) {
int p_remaining = round ( ( 1.0 - r ) * ( p_size . width - mp ) ) ;
2022-09-01 17:52:49 +02:00
draw_style_box ( theme_cache . progress_fg_style , Rect2 ( p_pos + Point2 ( p_remaining , 0 ) , Size2 ( p + theme_cache . progress_fg_style - > get_minimum_size ( ) . width , p_size . height ) ) ) ;
2022-05-18 09:17:55 +02:00
} else {
2022-09-01 17:52:49 +02:00
draw_style_box ( theme_cache . progress_fg_style , Rect2 ( p_pos , Size2 ( p + theme_cache . progress_fg_style - > get_minimum_size ( ) . width , p_size . height ) ) ) ;
2022-05-18 09:17:55 +02:00
}
}
}
// Draw main text.
Rect2 text_rect = _get_text_rect ( ) ;
2020-10-02 14:02:02 +02:00
float vofs = vscroll - > get_value ( ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
// Search for the first line.
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
int from_line = _find_first_line ( 0 , to_line , vofs ) ;
2014-02-10 02:10:30 +01:00
2022-09-01 17:52:49 +02:00
Point2 shadow_ofs ( theme_cache . shadow_offset_x , theme_cache . shadow_offset_y ) ;
2018-04-11 15:08:56 +02:00
2021-01-12 13:03:10 +01:00
visible_paragraph_count = 0 ;
2016-10-17 21:03:47 +02:00
visible_line_count = 0 ;
2018-04-11 15:08:56 +02:00
2020-10-02 14:02:02 +02:00
// New cache draw.
Point2 ofs = text_rect . get_position ( ) + Vector2 ( 0 , main - > lines [ from_line ] . offset . y - vofs ) ;
2021-12-05 13:28:32 +01:00
int processed_glyphs = 0 ;
2022-05-18 09:17:55 +02:00
while ( ofs . y < size . height & & from_line < to_line ) {
MutexLock lock ( main - > lines [ from_line ] . text_buf - > get_mutex ( ) ) ;
2021-01-12 13:03:10 +01:00
visible_paragraph_count + + ;
2022-09-01 17:52:49 +02:00
visible_line_count + = _draw_line ( main , from_line , ofs , text_rect . size . x , theme_cache . default_color , theme_cache . outline_size , theme_cache . font_outline_color , theme_cache . font_shadow_color , theme_cache . shadow_outline_size , shadow_ofs , processed_glyphs ) ;
ofs . y + = main - > lines [ from_line ] . text_buf - > get_size ( ) . y + main - > lines [ from_line ] . text_buf - > get_line_count ( ) * theme_cache . line_separation ;
2014-02-10 02:10:30 +01:00
from_line + + ;
}
2018-11-10 03:44:58 +01:00
} break ;
2022-02-15 18:06:48 +01:00
2018-11-10 03:44:58 +01:00
case NOTIFICATION_INTERNAL_PROCESS : {
2021-05-14 04:48:35 +02:00
if ( is_visible_in_tree ( ) ) {
2022-05-18 09:17:55 +02:00
if ( ! is_ready ( ) ) {
return ;
}
2021-02-02 03:16:37 +01:00
double dt = get_process_delta_time ( ) ;
2021-05-14 04:48:35 +02:00
_update_fx ( main , dt ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-05-14 04:48:35 +02:00
}
2021-10-21 23:02:46 +02:00
} break ;
2022-02-15 18:06:48 +01:00
2021-10-21 23:02:46 +02:00
case NOTIFICATION_FOCUS_EXIT : {
if ( deselect_on_focus_loss_enabled ) {
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2021-10-21 23:02:46 +02:00
}
} break ;
2022-02-15 18:06:48 +01:00
2021-11-21 19:26:15 +01:00
case NOTIFICATION_DRAG_END : {
selection . drag_attempt = false ;
} break ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
Control : : CursorShape RichTextLabel : : get_cursor_shape ( const Point2 & p_pos ) const {
2020-05-14 16:41:43 +02:00
if ( ! underline_meta ) {
2021-08-24 16:07:42 +02:00
return get_default_cursor_shape ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( selection . click_item ) {
2019-07-26 15:24:02 +02:00
return CURSOR_IBEAM ;
2020-05-14 16:41:43 +02:00
}
2019-07-26 15:24:02 +02:00
2020-04-02 01:20:12 +02:00
Item * item = nullptr ;
2020-10-02 14:02:02 +02:00
bool outside = true ;
2022-08-23 08:57:22 +02:00
const_cast < RichTextLabel * > ( this ) - > _find_click ( main , p_pos , nullptr , nullptr , & item , nullptr , & outside , true ) ;
2019-04-23 16:10:15 +02:00
2022-04-05 12:40:26 +02:00
if ( item & & ! outside & & const_cast < RichTextLabel * > ( this ) - > _find_meta ( item , nullptr ) ) {
2019-04-23 16:10:15 +02:00
return CURSOR_POINTING_HAND ;
2020-05-14 16:41:43 +02:00
}
2021-08-24 16:07:42 +02:00
return get_default_cursor_shape ( ) ;
2014-02-10 02:10:30 +01:00
}
2021-08-22 17:37:22 +02:00
void RichTextLabel : : gui_input ( const Ref < InputEvent > & p_event ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_event . is_null ( ) ) ;
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseButton > b = p_event ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( b . is_valid ( ) ) {
2021-08-13 23:31:57 +02:00
if ( b - > get_button_index ( ) = = MouseButton : : LEFT ) {
2021-04-13 10:25:44 +02:00
if ( b - > is_pressed ( ) & & ! b - > is_double_click ( ) ) {
2019-06-26 15:08:25 +02:00
scroll_updated = false ;
2020-10-02 14:02:02 +02:00
ItemFrame * c_frame = nullptr ;
int c_line = 0 ;
Item * c_item = nullptr ;
int c_index = 0 ;
2019-06-26 15:08:25 +02:00
bool outside ;
2014-02-10 02:10:30 +01:00
2021-11-21 19:26:15 +01:00
selection . drag_attempt = false ;
2022-08-23 08:57:22 +02:00
_find_click ( main , b - > get_position ( ) , & c_frame , & c_line , & c_item , & c_index , & outside , false ) ;
2020-10-02 14:02:02 +02:00
if ( c_item ! = nullptr ) {
2019-06-26 15:08:25 +02:00
if ( selection . enabled ) {
2020-10-02 14:02:02 +02:00
selection . click_frame = c_frame ;
selection . click_item = c_item ;
selection . click_line = c_line ;
selection . click_char = c_index ;
2017-12-04 16:46:20 +01:00
2019-06-26 15:08:25 +02:00
// Erase previous selection.
if ( selection . active ) {
2021-11-21 19:26:15 +01:00
if ( _is_click_inside_selection ( ) ) {
selection . drag_attempt = true ;
selection . click_item = nullptr ;
} else {
selection . from_frame = nullptr ;
selection . from_line = 0 ;
selection . from_item = nullptr ;
selection . from_char = 0 ;
selection . to_frame = nullptr ;
selection . to_line = 0 ;
selection . to_item = nullptr ;
selection . to_char = 0 ;
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2021-11-21 19:26:15 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-05-20 17:38:03 +02:00
}
2019-06-26 15:08:25 +02:00
}
2021-04-13 10:25:44 +02:00
} else if ( b - > is_pressed ( ) & & b - > is_double_click ( ) & & selection . enabled ) {
//double_click: select word
2020-10-02 14:02:02 +02:00
ItemFrame * c_frame = nullptr ;
int c_line = 0 ;
Item * c_item = nullptr ;
int c_index = 0 ;
2019-06-26 15:08:25 +02:00
bool outside ;
2017-12-23 09:59:54 +01:00
2021-11-21 19:26:15 +01:00
selection . drag_attempt = false ;
2022-08-23 08:57:22 +02:00
_find_click ( main , b - > get_position ( ) , & c_frame , & c_line , & c_item , & c_index , & outside , false ) ;
2017-12-23 09:59:54 +01:00
2020-10-02 14:02:02 +02:00
if ( c_frame ) {
const Line & l = c_frame - > lines [ c_line ] ;
2022-05-18 09:17:55 +02:00
MutexLock lock ( l . text_buf - > get_mutex ( ) ) ;
2021-08-27 23:19:51 +02:00
PackedInt32Array words = TS - > shaped_text_get_word_breaks ( l . text_buf - > get_rid ( ) ) ;
for ( int i = 0 ; i < words . size ( ) ; i = i + 2 ) {
if ( c_index > = words [ i ] & & c_index < words [ i + 1 ] ) {
2020-10-02 14:02:02 +02:00
selection . from_frame = c_frame ;
selection . from_line = c_line ;
selection . from_item = c_item ;
2021-08-27 23:19:51 +02:00
selection . from_char = words [ i ] ;
2017-12-23 09:59:54 +01:00
2020-10-02 14:02:02 +02:00
selection . to_frame = c_frame ;
selection . to_line = c_line ;
selection . to_item = c_item ;
2021-08-27 23:19:51 +02:00
selection . to_char = words [ i + 1 ] ;
2020-10-02 14:02:02 +02:00
selection . active = true ;
2021-10-20 21:25:09 +02:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
DisplayServer : : get_singleton ( ) - > clipboard_set_primary ( get_selected_text ( ) ) ;
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-10-02 14:02:02 +02:00
break ;
}
2017-12-23 09:59:54 +01:00
}
2019-06-26 15:08:25 +02:00
}
} else if ( ! b - > is_pressed ( ) ) {
2021-10-20 21:25:09 +02:00
if ( selection . enabled & & DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_CLIPBOARD_PRIMARY ) ) {
2021-10-12 09:43:50 +02:00
DisplayServer : : get_singleton ( ) - > clipboard_set_primary ( get_selected_text ( ) ) ;
}
2020-10-02 14:02:02 +02:00
selection . click_item = nullptr ;
2021-11-21 19:26:15 +01:00
if ( selection . drag_attempt ) {
selection . drag_attempt = false ;
if ( _is_click_inside_selection ( ) ) {
selection . from_frame = nullptr ;
selection . from_line = 0 ;
selection . from_item = nullptr ;
selection . from_char = 0 ;
selection . to_frame = nullptr ;
selection . to_line = 0 ;
selection . to_item = nullptr ;
selection . to_char = 0 ;
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2021-11-21 19:26:15 +01:00
}
}
2022-05-19 10:55:14 +02:00
if ( ! b - > is_double_click ( ) & & ! scroll_updated & & ! selection . active ) {
2020-10-02 14:02:02 +02:00
Item * c_item = nullptr ;
2018-10-09 02:04:59 +02:00
2020-10-02 14:02:02 +02:00
bool outside = true ;
2022-08-23 08:57:22 +02:00
_find_click ( main , b - > get_position ( ) , nullptr , nullptr , & c_item , nullptr , & outside , true ) ;
2018-10-09 02:04:59 +02:00
2020-10-02 14:02:02 +02:00
if ( c_item ) {
2019-06-26 15:08:25 +02:00
Variant meta ;
2020-10-02 14:02:02 +02:00
if ( ! outside & & _find_meta ( c_item , & meta ) ) {
2019-06-26 15:08:25 +02:00
//meta clicked
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " meta_clicked " ) , meta ) ;
2018-10-09 02:04:59 +02:00
}
}
2014-02-10 02:10:30 +01:00
}
}
2017-05-20 17:38:03 +02:00
}
2014-02-10 02:10:30 +01:00
2021-08-13 23:31:57 +02:00
if ( b - > get_button_index ( ) = = MouseButton : : WHEEL_UP ) {
2020-05-14 16:41:43 +02:00
if ( scroll_active ) {
2017-05-20 17:38:03 +02:00
vscroll - > set_value ( vscroll - > get_value ( ) - vscroll - > get_page ( ) * b - > get_factor ( ) * 0.5 / 8 ) ;
2020-05-14 16:41:43 +02:00
}
2017-05-20 17:38:03 +02:00
}
2021-08-13 23:31:57 +02:00
if ( b - > get_button_index ( ) = = MouseButton : : WHEEL_DOWN ) {
2020-05-14 16:41:43 +02:00
if ( scroll_active ) {
2017-05-20 17:38:03 +02:00
vscroll - > set_value ( vscroll - > get_value ( ) + vscroll - > get_page ( ) * b - > get_factor ( ) * 0.5 / 8 ) ;
2020-05-14 16:41:43 +02:00
}
2017-05-20 17:38:03 +02:00
}
2022-04-12 08:58:04 +02:00
if ( b - > get_button_index ( ) = = MouseButton : : RIGHT & & context_menu_enabled ) {
_generate_context_menu ( ) ;
menu - > set_position ( get_screen_position ( ) + b - > get_position ( ) ) ;
menu - > reset_size ( ) ;
menu - > popup ( ) ;
grab_focus ( ) ;
}
2017-05-20 17:38:03 +02:00
}
2014-02-16 01:16:33 +01:00
2017-11-01 21:49:39 +01:00
Ref < InputEventPanGesture > pan_gesture = p_event ;
if ( pan_gesture . is_valid ( ) ) {
2020-05-14 16:41:43 +02:00
if ( scroll_active ) {
2017-11-01 21:49:39 +01:00
vscroll - > set_value ( vscroll - > get_value ( ) + vscroll - > get_page ( ) * pan_gesture - > get_delta ( ) . y * 0.5 / 8 ) ;
2020-05-14 16:41:43 +02:00
}
2017-11-01 21:49:39 +01:00
return ;
}
2017-05-20 17:38:03 +02:00
Ref < InputEventKey > k = p_event ;
if ( k . is_valid ( ) ) {
2020-12-07 12:32:00 +01:00
if ( k - > is_pressed ( ) ) {
2020-02-14 23:15:38 +01:00
bool handled = false ;
2014-02-16 01:16:33 +01:00
2021-02-24 13:25:15 +01:00
if ( k - > is_action ( " ui_page_up " ) & & vscroll - > is_visible_in_tree ( ) ) {
2020-12-07 12:32:00 +01:00
vscroll - > set_value ( vscroll - > get_value ( ) - vscroll - > get_page ( ) ) ;
handled = true ;
}
2021-02-24 13:25:15 +01:00
if ( k - > is_action ( " ui_page_down " ) & & vscroll - > is_visible_in_tree ( ) ) {
2020-12-07 12:32:00 +01:00
vscroll - > set_value ( vscroll - > get_value ( ) + vscroll - > get_page ( ) ) ;
handled = true ;
}
if ( k - > is_action ( " ui_up " ) & & vscroll - > is_visible_in_tree ( ) ) {
2022-09-01 17:52:49 +02:00
vscroll - > set_value ( vscroll - > get_value ( ) - theme_cache . normal_font - > get_height ( theme_cache . normal_font_size ) ) ;
2020-12-07 12:32:00 +01:00
handled = true ;
}
if ( k - > is_action ( " ui_down " ) & & vscroll - > is_visible_in_tree ( ) ) {
2022-09-01 17:52:49 +02:00
vscroll - > set_value ( vscroll - > get_value ( ) + theme_cache . normal_font - > get_height ( theme_cache . normal_font_size ) ) ;
2020-12-07 12:32:00 +01:00
handled = true ;
}
if ( k - > is_action ( " ui_home " ) & & vscroll - > is_visible_in_tree ( ) ) {
vscroll - > set_value ( 0 ) ;
handled = true ;
}
if ( k - > is_action ( " ui_end " ) & & vscroll - > is_visible_in_tree ( ) ) {
vscroll - > set_value ( vscroll - > get_max ( ) ) ;
handled = true ;
}
2022-04-12 08:58:04 +02:00
if ( is_shortcut_keys_enabled ( ) ) {
if ( k - > is_action ( " ui_text_select_all " ) ) {
select_all ( ) ;
handled = true ;
}
if ( k - > is_action ( " ui_copy " ) ) {
selection_copy ( ) ;
handled = true ;
}
2022-04-10 18:03:10 +02:00
}
2022-04-12 08:58:04 +02:00
if ( k - > is_action ( " ui_menu " , true ) ) {
if ( context_menu_enabled ) {
_generate_context_menu ( ) ;
menu - > set_position ( get_screen_position ( ) ) ;
menu - > reset_size ( ) ;
menu - > popup ( ) ;
menu - > grab_focus ( ) ;
}
2020-12-07 12:32:00 +01:00
handled = true ;
2014-02-16 01:16:33 +01:00
}
2020-05-14 16:41:43 +02:00
if ( handled ) {
2017-05-20 17:38:03 +02:00
accept_event ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-05-20 17:38:03 +02:00
}
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > m = p_event ;
if ( m . is_valid ( ) ) {
2020-10-02 14:02:02 +02:00
ItemFrame * c_frame = nullptr ;
int c_line = 0 ;
Item * c_item = nullptr ;
int c_index = 0 ;
2017-11-17 06:49:39 +01:00
bool outside ;
2014-02-10 02:10:30 +01:00
2022-08-23 08:57:22 +02:00
_find_click ( main , m - > get_position ( ) , & c_frame , & c_line , & c_item , & c_index , & outside , false ) ;
2020-10-02 14:02:02 +02:00
if ( selection . click_item & & c_item ) {
selection . from_frame = selection . click_frame ;
selection . from_line = selection . click_line ;
selection . from_item = selection . click_item ;
2017-05-20 17:38:03 +02:00
selection . from_char = selection . click_char ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
selection . to_frame = c_frame ;
selection . to_line = c_line ;
selection . to_item = c_item ;
selection . to_char = c_index ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
bool swap = false ;
2022-02-09 17:27:39 +01:00
if ( selection . click_frame & & c_frame ) {
const Line & l1 = c_frame - > lines [ c_line ] ;
const Line & l2 = selection . click_frame - > lines [ selection . click_line ] ;
if ( l1 . char_offset + c_index < l2 . char_offset + selection . click_char ) {
2017-03-05 16:44:50 +01:00
swap = true ;
2022-02-09 17:27:39 +01:00
} else if ( l1 . char_offset + c_index = = l2 . char_offset + selection . click_char ) {
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2017-05-20 17:38:03 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2017-05-20 17:38:03 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( swap ) {
2020-10-02 14:02:02 +02:00
SWAP ( selection . from_frame , selection . to_frame ) ;
SWAP ( selection . from_line , selection . to_line ) ;
SWAP ( selection . from_item , selection . to_item ) ;
2017-05-20 17:38:03 +02:00
SWAP ( selection . from_char , selection . to_char ) ;
2014-02-10 02:10:30 +01:00
}
2017-05-20 17:38:03 +02:00
selection . active = true ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2017-05-20 17:38:03 +02:00
}
2017-11-17 06:49:39 +01:00
Variant meta ;
2019-02-21 23:23:05 +01:00
ItemMeta * item_meta ;
2020-10-02 14:02:02 +02:00
if ( c_item & & ! outside & & _find_meta ( c_item , & meta , & item_meta ) ) {
2019-02-21 23:23:05 +01:00
if ( meta_hovering ! = item_meta ) {
2017-11-17 06:49:39 +01:00
if ( meta_hovering ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " meta_hover_ended " ) , current_meta ) ;
2017-11-17 06:49:39 +01:00
}
2019-02-21 23:23:05 +01:00
meta_hovering = item_meta ;
2017-11-17 06:49:39 +01:00
current_meta = meta ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " meta_hover_started " ) , meta ) ;
2017-11-17 06:49:39 +01:00
}
} else if ( meta_hovering ) {
2020-04-02 01:20:12 +02:00
meta_hovering = nullptr ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " meta_hover_ended " ) , current_meta ) ;
2017-11-17 06:49:39 +01:00
current_meta = false ;
}
2014-02-10 02:10:30 +01:00
}
}
2022-02-21 18:34:16 +01:00
String RichTextLabel : : get_tooltip ( const Point2 & p_pos ) const {
Item * c_item = nullptr ;
bool outside ;
2022-08-23 08:57:22 +02:00
const_cast < RichTextLabel * > ( this ) - > _find_click ( main , p_pos , nullptr , nullptr , & c_item , nullptr , & outside , true ) ;
2022-02-21 18:34:16 +01:00
String description ;
if ( c_item & & ! outside & & const_cast < RichTextLabel * > ( this ) - > _find_hint ( c_item , & description ) ) {
return description ;
} else {
return Control : : get_tooltip ( p_pos ) ;
}
}
2020-10-02 14:02:02 +02:00
void RichTextLabel : : _find_frame ( Item * p_item , ItemFrame * * r_frame , int * r_line ) {
if ( r_frame ! = nullptr ) {
* r_frame = nullptr ;
}
if ( r_line ! = nullptr ) {
* r_line = 0 ;
}
Item * item = p_item ;
while ( item ) {
if ( item - > parent ! = nullptr & & item - > parent - > type = = ITEM_FRAME ) {
if ( r_frame ! = nullptr ) {
2022-04-05 12:40:26 +02:00
* r_frame = static_cast < ItemFrame * > ( item - > parent ) ;
2020-10-02 14:02:02 +02:00
}
if ( r_line ! = nullptr ) {
* r_line = item - > line ;
}
return ;
}
item = item - > parent ;
}
}
2022-07-06 14:16:50 +02:00
RichTextLabel : : Item * RichTextLabel : : _find_indentable ( Item * p_item ) {
Item * indentable = p_item ;
while ( indentable ) {
if ( indentable - > type = = ITEM_INDENT | | indentable - > type = = ITEM_LIST ) {
return indentable ;
}
indentable = indentable - > parent ;
}
return indentable ;
}
2022-07-26 09:45:40 +02:00
RichTextLabel : : ItemFont * RichTextLabel : : _find_font ( Item * p_item ) {
2017-03-05 16:44:50 +01:00
Item * fontitem = p_item ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( fontitem ) {
if ( fontitem - > type = = ITEM_FONT ) {
ItemFont * fi = static_cast < ItemFont * > ( fontitem ) ;
2022-07-26 09:45:40 +02:00
return fi ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
fontitem = fontitem - > parent ;
2014-02-10 02:10:30 +01:00
}
2022-07-26 09:45:40 +02:00
return nullptr ;
2014-02-10 02:10:30 +01:00
}
2022-07-26 09:45:40 +02:00
RichTextLabel : : ItemFontSize * RichTextLabel : : _find_font_size ( Item * p_item ) {
2020-10-02 14:02:02 +02:00
Item * sizeitem = p_item ;
while ( sizeitem ) {
if ( sizeitem - > type = = ITEM_FONT_SIZE ) {
ItemFontSize * fi = static_cast < ItemFontSize * > ( sizeitem ) ;
2022-07-26 09:45:40 +02:00
return fi ;
2020-10-02 14:02:02 +02:00
}
sizeitem = sizeitem - > parent ;
}
2022-07-26 09:45:40 +02:00
return nullptr ;
2020-10-02 14:02:02 +02:00
}
2020-12-25 22:45:28 +01:00
int RichTextLabel : : _find_outline_size ( Item * p_item , int p_default ) {
2020-10-02 14:02:02 +02:00
Item * sizeitem = p_item ;
while ( sizeitem ) {
if ( sizeitem - > type = = ITEM_OUTLINE_SIZE ) {
ItemOutlineSize * fi = static_cast < ItemOutlineSize * > ( sizeitem ) ;
return fi - > outline_size ;
}
sizeitem = sizeitem - > parent ;
}
2020-12-25 22:45:28 +01:00
return p_default ;
2020-10-02 14:02:02 +02:00
}
2020-11-19 15:45:23 +01:00
RichTextLabel : : ItemDropcap * RichTextLabel : : _find_dc_item ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_DROPCAP ) {
return static_cast < ItemDropcap * > ( item ) ;
}
item = item - > parent ;
}
return nullptr ;
}
2020-10-02 14:02:02 +02:00
RichTextLabel : : ItemList * RichTextLabel : : _find_list_item ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_LIST ) {
return static_cast < ItemList * > ( item ) ;
}
item = item - > parent ;
}
return nullptr ;
}
int RichTextLabel : : _find_list ( Item * p_item , Vector < int > & r_index , Vector < ItemList * > & r_list ) {
Item * item = p_item ;
Item * prev_item = p_item ;
int level = 0 ;
while ( item ) {
if ( item - > type = = ITEM_LIST ) {
ItemList * list = static_cast < ItemList * > ( item ) ;
ItemFrame * frame = nullptr ;
int line = - 1 ;
_find_frame ( list , & frame , & line ) ;
int index = 1 ;
if ( frame ! = nullptr ) {
for ( int i = list - > line + 1 ; i < = prev_item - > line ; i + + ) {
if ( _find_list_item ( frame - > lines [ i ] . from ) = = list ) {
index + + ;
}
}
}
r_index . push_back ( index ) ;
r_list . push_back ( list ) ;
prev_item = item ;
}
level + + ;
item = item - > parent ;
}
return level ;
}
int RichTextLabel : : _find_margin ( Item * p_item , const Ref < Font > & p_base_font , int p_base_font_size ) {
2017-03-05 16:44:50 +01:00
Item * item = p_item ;
2014-02-10 02:10:30 +01:00
2021-02-09 18:24:36 +01:00
float margin = 0.0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( item ) {
if ( item - > type = = ITEM_INDENT ) {
2022-07-26 09:45:40 +02:00
Ref < Font > font = p_base_font ;
int font_size = p_base_font_size ;
ItemFont * font_it = _find_font ( item ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2020-05-14 16:41:43 +02:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( item ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2020-10-02 14:02:02 +02:00
}
2022-05-09 11:47:10 +02:00
margin + = tab_size * font - > get_char_size ( ' ' , font_size ) . width ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else if ( item - > type = = ITEM_LIST ) {
2022-07-26 09:45:40 +02:00
Ref < Font > font = p_base_font ;
int font_size = p_base_font_size ;
ItemFont * font_it = _find_font ( item ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2020-05-14 16:41:43 +02:00
}
2022-07-26 09:45:40 +02:00
ItemFontSize * font_size_it = _find_font_size ( item ) ;
if ( font_size_it & & font_size_it - > font_size > 0 ) {
font_size = font_size_it - > font_size ;
2020-10-02 14:02:02 +02:00
}
2022-05-09 11:47:10 +02:00
margin + = tab_size * font - > get_char_size ( ' ' , font_size ) . width ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
item = item - > parent ;
2014-02-10 02:10:30 +01:00
}
return margin ;
}
2021-11-25 03:58:47 +01:00
HorizontalAlignment RichTextLabel : : _find_alignment ( Item * p_item ) {
2017-03-05 16:44:50 +01:00
Item * item = p_item ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( item ) {
2020-10-02 14:02:02 +02:00
if ( item - > type = = ITEM_PARAGRAPH ) {
ItemParagraph * p = static_cast < ItemParagraph * > ( item ) ;
2021-11-25 03:58:47 +01:00
return p - > alignment ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
item = item - > parent ;
2014-02-10 02:10:30 +01:00
}
2021-11-25 03:58:47 +01:00
return default_alignment ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
TextServer : : Direction RichTextLabel : : _find_direction ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_PARAGRAPH ) {
ItemParagraph * p = static_cast < ItemParagraph * > ( item ) ;
if ( p - > direction ! = Control : : TEXT_DIRECTION_INHERITED ) {
return ( TextServer : : Direction ) p - > direction ;
}
}
item = item - > parent ;
}
if ( text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
return is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ;
} else {
return ( TextServer : : Direction ) text_direction ;
}
}
2022-04-19 12:27:18 +02:00
TextServer : : StructuredTextParser RichTextLabel : : _find_stt ( Item * p_item ) {
2020-10-02 14:02:02 +02:00
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_PARAGRAPH ) {
ItemParagraph * p = static_cast < ItemParagraph * > ( item ) ;
return p - > st_parser ;
}
item = item - > parent ;
}
return st_parser ;
}
String RichTextLabel : : _find_language ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_PARAGRAPH ) {
ItemParagraph * p = static_cast < ItemParagraph * > ( item ) ;
return p - > language ;
}
item = item - > parent ;
}
return language ;
}
2017-03-05 16:44:50 +01:00
Color RichTextLabel : : _find_color ( Item * p_item , const Color & p_default_color ) {
Item * item = p_item ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( item ) {
if ( item - > type = = ITEM_COLOR ) {
ItemColor * color = static_cast < ItemColor * > ( item ) ;
2014-02-10 02:10:30 +01:00
return color - > color ;
}
2017-03-05 16:44:50 +01:00
item = item - > parent ;
2014-02-10 02:10:30 +01:00
}
return p_default_color ;
}
2020-10-02 14:02:02 +02:00
Color RichTextLabel : : _find_outline_color ( Item * p_item , const Color & p_default_color ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_OUTLINE_COLOR ) {
ItemOutlineColor * color = static_cast < ItemOutlineColor * > ( item ) ;
return color - > color ;
}
item = item - > parent ;
}
return p_default_color ;
}
2014-02-10 02:10:30 +01:00
bool RichTextLabel : : _find_underline ( Item * p_item ) {
2017-03-05 16:44:50 +01:00
Item * item = p_item ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( item ) {
if ( item - > type = = ITEM_UNDERLINE ) {
2014-02-10 02:10:30 +01:00
return true ;
}
2017-03-05 16:44:50 +01:00
item = item - > parent ;
2014-02-10 02:10:30 +01:00
}
return false ;
}
2018-09-22 23:41:13 +02:00
bool RichTextLabel : : _find_strikethrough ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_STRIKETHROUGH ) {
return true ;
}
item = item - > parent ;
}
return false ;
}
2019-09-25 22:05:42 +02:00
void RichTextLabel : : _fetch_item_fx_stack ( Item * p_item , Vector < ItemFX * > & r_stack ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_CUSTOMFX | | item - > type = = ITEM_SHAKE | | item - > type = = ITEM_WAVE | | item - > type = = ITEM_TORNADO | | item - > type = = ITEM_RAINBOW ) {
r_stack . push_back ( static_cast < ItemFX * > ( item ) ) ;
}
item = item - > parent ;
}
}
2018-11-10 03:44:58 +01:00
bool RichTextLabel : : _find_meta ( Item * p_item , Variant * r_meta , ItemMeta * * r_item ) {
2017-03-05 16:44:50 +01:00
Item * item = p_item ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( item ) {
if ( item - > type = = ITEM_META ) {
ItemMeta * meta = static_cast < ItemMeta * > ( item ) ;
2020-05-14 16:41:43 +02:00
if ( r_meta ) {
2017-03-05 16:44:50 +01:00
* r_meta = meta - > meta ;
2020-05-14 16:41:43 +02:00
}
if ( r_item ) {
2019-02-21 23:23:05 +01:00
* r_item = meta ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
2017-03-05 16:44:50 +01:00
item = item - > parent ;
2014-02-10 02:10:30 +01:00
}
return false ;
}
2022-02-21 18:34:16 +01:00
bool RichTextLabel : : _find_hint ( Item * p_item , String * r_description ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_HINT ) {
ItemHint * hint = static_cast < ItemHint * > ( item ) ;
if ( r_description ) {
* r_description = hint - > description ;
}
return true ;
}
item = item - > parent ;
}
return false ;
}
2020-01-26 23:11:09 +01:00
Color RichTextLabel : : _find_bgcolor ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_BGCOLOR ) {
ItemBGColor * color = static_cast < ItemBGColor * > ( item ) ;
return color - > color ;
}
item = item - > parent ;
}
return Color ( 0 , 0 , 0 , 0 ) ;
}
Color RichTextLabel : : _find_fgcolor ( Item * p_item ) {
Item * item = p_item ;
while ( item ) {
if ( item - > type = = ITEM_FGCOLOR ) {
ItemFGColor * color = static_cast < ItemFGColor * > ( item ) ;
return color - > color ;
}
item = item - > parent ;
}
return Color ( 0 , 0 , 0 , 0 ) ;
}
2019-06-20 12:42:25 +02:00
bool RichTextLabel : : _find_layout_subitem ( Item * from , Item * to ) {
if ( from & & from ! = to ) {
2020-05-14 16:41:43 +02:00
if ( from - > type ! = ITEM_FONT & & from - > type ! = ITEM_COLOR & & from - > type ! = ITEM_UNDERLINE & & from - > type ! = ITEM_STRIKETHROUGH ) {
2019-06-20 12:42:25 +02:00
return true ;
2020-05-14 16:41:43 +02:00
}
2019-06-20 12:42:25 +02:00
2021-07-16 05:45:57 +02:00
for ( Item * E : from - > subitems ) {
bool layout = _find_layout_subitem ( E , to ) ;
2020-10-02 14:02:02 +02:00
if ( layout ) {
return true ;
}
}
}
return false ;
}
2022-05-18 09:17:55 +02:00
void RichTextLabel : : _thread_function ( void * self ) {
RichTextLabel * rtl = reinterpret_cast < RichTextLabel * > ( self ) ;
rtl - > _process_line_caches ( ) ;
rtl - > updating . store ( false ) ;
2022-08-13 23:21:24 +02:00
rtl - > call_deferred ( SNAME ( " queue_redraw " ) ) ;
2022-05-18 09:17:55 +02:00
}
void RichTextLabel : : _stop_thread ( ) {
if ( threaded ) {
stop_thread . store ( true ) ;
thread . wait_to_finish ( ) ;
}
}
bool RichTextLabel : : is_ready ( ) const {
if ( updating . load ( ) ) {
return false ;
}
return ( main - > first_invalid_line . load ( ) = = ( int ) main - > lines . size ( ) & & main - > first_resized_line . load ( ) = = ( int ) main - > lines . size ( ) & & main - > first_invalid_font_line . load ( ) = = ( int ) main - > lines . size ( ) ) ;
}
void RichTextLabel : : set_threaded ( bool p_threaded ) {
if ( threaded ! = p_threaded ) {
_stop_thread ( ) ;
threaded = p_threaded ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-05-18 09:17:55 +02:00
}
}
bool RichTextLabel : : is_threaded ( ) const {
return threaded ;
}
void RichTextLabel : : set_progress_bar_delay ( int p_delay_ms ) {
progress_delay = p_delay_ms ;
}
int RichTextLabel : : get_progress_bar_delay ( ) const {
return progress_delay ;
}
bool RichTextLabel : : _validate_line_caches ( ) {
if ( updating . load ( ) ) {
return false ;
}
if ( main - > first_invalid_line . load ( ) = = ( int ) main - > lines . size ( ) ) {
MutexLock data_lock ( data_mutex ) ;
Rect2 text_rect = _get_text_rect ( ) ;
int ctrl_height = get_size ( ) . height ;
2022-01-20 08:30:42 +01:00
// Update fonts.
2022-05-18 09:17:55 +02:00
if ( main - > first_invalid_font_line . load ( ) ! = ( int ) main - > lines . size ( ) ) {
for ( int i = main - > first_invalid_font_line . load ( ) ; i < ( int ) main - > lines . size ( ) ; i + + ) {
2022-09-01 17:52:49 +02:00
_update_line_font ( main , i , theme_cache . normal_font , theme_cache . normal_font_size ) ;
2022-01-20 08:30:42 +01:00
}
2022-05-18 09:17:55 +02:00
main - > first_resized_line . store ( main - > first_invalid_font_line . load ( ) ) ;
main - > first_invalid_font_line . store ( main - > lines . size ( ) ) ;
2022-01-20 08:30:42 +01:00
}
2022-05-18 09:17:55 +02:00
if ( main - > first_resized_line . load ( ) = = ( int ) main - > lines . size ( ) ) {
return true ;
2020-10-02 14:02:02 +02:00
}
// Resize lines without reshaping.
2022-05-18 09:17:55 +02:00
int fi = main - > first_resized_line . load ( ) ;
2022-06-05 12:58:04 +02:00
float total_height = ( fi = = 0 ) ? 0 : _calculate_line_vertical_offset ( main - > lines [ fi - 1 ] ) ;
2022-05-18 09:17:55 +02:00
for ( int i = fi ; i < ( int ) main - > lines . size ( ) ; i + + ) {
2022-09-01 17:52:49 +02:00
total_height = _resize_line ( main , i , theme_cache . normal_font , theme_cache . normal_font_size , text_rect . get_size ( ) . width - scroll_w , total_height ) ;
2022-05-18 09:17:55 +02:00
updating_scroll = true ;
bool exceeds = total_height > ctrl_height & & scroll_active ;
if ( exceeds ! = scroll_visible ) {
if ( exceeds ) {
scroll_visible = true ;
scroll_w = vscroll - > get_combined_minimum_size ( ) . width ;
vscroll - > show ( ) ;
vscroll - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_END , - scroll_w ) ;
} else {
scroll_visible = false ;
scroll_w = 0 ;
vscroll - > hide ( ) ;
}
2020-10-02 14:02:02 +02:00
2022-05-18 09:17:55 +02:00
main - > first_resized_line . store ( 0 ) ;
2020-10-02 14:02:02 +02:00
2022-05-18 09:17:55 +02:00
total_height = 0 ;
for ( int j = 0 ; j < = i ; j + + ) {
2022-09-01 17:52:49 +02:00
total_height = _resize_line ( main , j , theme_cache . normal_font , theme_cache . normal_font_size , text_rect . get_size ( ) . width - scroll_w , total_height ) ;
2022-05-18 09:17:55 +02:00
main - > first_resized_line . store ( j ) ;
}
}
2020-10-02 14:02:02 +02:00
2022-05-18 09:17:55 +02:00
vscroll - > set_max ( total_height ) ;
vscroll - > set_page ( text_rect . size . height ) ;
if ( scroll_follow & & scroll_following ) {
vscroll - > set_value ( total_height ) ;
}
updating_scroll = false ;
2019-06-20 12:42:25 +02:00
2022-05-18 09:17:55 +02:00
main - > first_resized_line . store ( i ) ;
2019-06-20 12:42:25 +02:00
}
2022-05-18 09:17:55 +02:00
main - > first_resized_line . store ( main - > lines . size ( ) ) ;
2019-06-20 12:42:25 +02:00
2020-10-02 14:02:02 +02:00
if ( fit_content_height ) {
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2020-10-02 14:02:02 +02:00
}
2022-05-18 09:17:55 +02:00
return true ;
2020-05-14 16:41:43 +02:00
}
2022-05-18 09:17:55 +02:00
stop_thread . store ( false ) ;
if ( threaded ) {
updating . store ( true ) ;
loaded . store ( true ) ;
thread . start ( RichTextLabel : : _thread_function , reinterpret_cast < void * > ( this ) ) ;
loading_started = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
set_physics_process_internal ( true ) ;
return false ;
} else {
_process_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-05-18 09:17:55 +02:00
return true ;
}
}
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
void RichTextLabel : : _process_line_caches ( ) {
2020-10-02 14:02:02 +02:00
// Shape invalid lines.
2022-06-06 22:45:01 +02:00
if ( ! is_inside_tree ( ) ) {
return ;
}
2022-05-18 09:17:55 +02:00
MutexLock data_lock ( data_mutex ) ;
2017-09-02 18:20:04 +02:00
Rect2 text_rect = _get_text_rect ( ) ;
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
int ctrl_height = get_size ( ) . height ;
int fi = main - > first_invalid_line . load ( ) ;
int total_chars = ( fi = = 0 ) ? 0 : ( main - > lines [ fi ] . char_offset + main - > lines [ fi ] . char_count ) ;
2014-02-10 02:10:30 +01:00
2022-06-05 12:58:04 +02:00
float total_height = ( fi = = 0 ) ? 0 : _calculate_line_vertical_offset ( main - > lines [ fi - 1 ] ) ;
2022-05-18 09:17:55 +02:00
for ( int i = fi ; i < ( int ) main - > lines . size ( ) ; i + + ) {
2022-09-01 17:52:49 +02:00
total_height = _shape_line ( main , i , theme_cache . normal_font , theme_cache . normal_font_size , text_rect . get_size ( ) . width - scroll_w , total_height , & total_chars ) ;
2022-05-18 09:17:55 +02:00
updating_scroll = true ;
bool exceeds = total_height > ctrl_height & & scroll_active ;
if ( exceeds ! = scroll_visible ) {
if ( exceeds ) {
scroll_visible = true ;
scroll_w = vscroll - > get_combined_minimum_size ( ) . width ;
vscroll - > show ( ) ;
vscroll - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_END , - scroll_w ) ;
} else {
scroll_visible = false ;
scroll_w = 0 ;
vscroll - > hide ( ) ;
}
main - > first_invalid_line . store ( 0 ) ;
main - > first_resized_line . store ( 0 ) ;
main - > first_invalid_font_line . store ( 0 ) ;
2014-02-10 02:10:30 +01:00
2022-06-05 12:58:04 +02:00
// since scroll was added or removed we need to resize all lines
2022-05-18 09:17:55 +02:00
total_height = 0 ;
for ( int j = 0 ; j < = i ; j + + ) {
2022-09-01 17:52:49 +02:00
total_height = _resize_line ( main , j , theme_cache . normal_font , theme_cache . normal_font_size , text_rect . get_size ( ) . width - scroll_w , total_height ) ;
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( j ) ;
main - > first_resized_line . store ( j ) ;
main - > first_invalid_font_line . store ( j ) ;
}
}
vscroll - > set_max ( total_height ) ;
vscroll - > set_page ( text_rect . size . height ) ;
if ( scroll_follow & & scroll_following ) {
vscroll - > set_value ( total_height ) ;
}
updating_scroll = false ;
main - > first_invalid_line . store ( i ) ;
main - > first_resized_line . store ( i ) ;
main - > first_invalid_font_line . store ( i ) ;
if ( stop_thread . load ( ) ) {
return ;
}
loaded . store ( double ( i ) / double ( main - > lines . size ( ) ) ) ;
2020-05-14 16:41:43 +02:00
}
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( main - > lines . size ( ) ) ;
main - > first_resized_line . store ( main - > lines . size ( ) ) ;
main - > first_invalid_font_line . store ( main - > lines . size ( ) ) ;
2019-11-01 15:17:40 +01:00
if ( fit_content_height ) {
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2019-11-01 15:17:40 +01:00
}
2022-05-18 09:17:55 +02:00
emit_signal ( SNAME ( " finished " ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : _invalidate_current_line ( ItemFrame * p_frame ) {
2022-05-18 09:17:55 +02:00
if ( ( int ) p_frame - > lines . size ( ) - 1 < = p_frame - > first_invalid_line ) {
p_frame - > first_invalid_line = ( int ) p_frame - > lines . size ( ) - 1 ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : add_text ( const String & p_text ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-05-14 16:41:43 +02:00
if ( current - > type = = ITEM_TABLE ) {
2015-12-26 14:25:17 +01:00
return ; //can't add anything here
2020-05-14 16:41:43 +02:00
}
2015-12-26 14:25:17 +01:00
2017-03-05 16:44:50 +01:00
int pos = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( pos < p_text . length ( ) ) {
int end = p_text . find ( " \n " , pos ) ;
2014-02-10 02:10:30 +01:00
String line ;
2017-03-05 16:44:50 +01:00
bool eol = false ;
if ( end = = - 1 ) {
end = p_text . length ( ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
eol = true ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( pos = = 0 & & end = = p_text . length ( ) ) {
2017-03-05 16:44:50 +01:00
line = p_text ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
line = p_text . substr ( pos , end - pos ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( line . length ( ) > 0 ) {
if ( current - > subitems . size ( ) & & current - > subitems . back ( ) - > get ( ) - > type = = ITEM_TEXT ) {
2014-02-10 02:10:30 +01:00
//append text condition!
2017-03-05 16:44:50 +01:00
ItemText * ti = static_cast < ItemText * > ( current - > subitems . back ( ) - > get ( ) ) ;
ti - > text + = line ;
2015-12-26 14:25:17 +01:00
_invalidate_current_line ( main ) ;
2014-02-10 02:10:30 +01:00
} else {
//append item condition
2017-03-05 16:44:50 +01:00
ItemText * item = memnew ( ItemText ) ;
item - > text = line ;
_add_item ( item , false ) ;
2014-02-10 02:10:30 +01:00
}
}
if ( eol ) {
2017-03-05 16:44:50 +01:00
ItemNewline * item = memnew ( ItemNewline ) ;
item - > line = current_frame - > lines . size ( ) ;
_add_item ( item , false ) ;
current_frame - > lines . resize ( current_frame - > lines . size ( ) + 1 ) ;
2020-05-14 16:41:43 +02:00
if ( item - > type ! = ITEM_NEWLINE ) {
2022-05-18 09:17:55 +02:00
current_frame - > lines [ current_frame - > lines . size ( ) - 1 ] . from = item ;
2020-05-14 16:41:43 +02:00
}
2015-12-26 14:25:17 +01:00
_invalidate_current_line ( current_frame ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
pos = end + 1 ;
2014-02-10 02:10:30 +01:00
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
2015-12-26 14:25:17 +01:00
void RichTextLabel : : _add_item ( Item * p_item , bool p_enter , bool p_ensure_newline ) {
2017-03-05 16:44:50 +01:00
p_item - > parent = current ;
p_item - > E = current - > subitems . push_back ( p_item ) ;
p_item - > index = current_idx + + ;
2020-10-02 14:02:02 +02:00
p_item - > char_ofs = current_char_ofs ;
if ( p_item - > type = = ITEM_TEXT ) {
2022-04-05 12:40:26 +02:00
ItemText * t = static_cast < ItemText * > ( p_item ) ;
2020-10-02 14:02:02 +02:00
current_char_ofs + = t - > text . length ( ) ;
} else if ( p_item - > type = = ITEM_IMAGE ) {
current_char_ofs + + ;
}
2015-12-26 14:25:17 +01:00
2020-05-14 16:41:43 +02:00
if ( p_enter ) {
2017-03-05 16:44:50 +01:00
current = p_item ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2019-06-14 22:38:33 +02:00
if ( p_ensure_newline ) {
Item * from = current_frame - > lines [ current_frame - > lines . size ( ) - 1 ] . from ;
// only create a new line for Item types that generate content/layout, ignore those that represent formatting/styling
2019-06-20 12:42:25 +02:00
if ( _find_layout_subitem ( from , p_item ) ) {
2019-06-14 22:38:33 +02:00
_invalidate_current_line ( current_frame ) ;
current_frame - > lines . resize ( current_frame - > lines . size ( ) + 1 ) ;
}
2015-12-26 14:25:17 +01:00
}
2020-04-02 01:20:12 +02:00
if ( current_frame - > lines [ current_frame - > lines . size ( ) - 1 ] . from = = nullptr ) {
2022-05-18 09:17:55 +02:00
current_frame - > lines [ current_frame - > lines . size ( ) - 1 ] . from = p_item ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
p_item - > line = current_frame - > lines . size ( ) - 1 ;
2014-02-10 02:10:30 +01:00
2015-12-26 14:25:17 +01:00
_invalidate_current_line ( current_frame ) ;
2020-03-20 21:51:53 +01:00
if ( fixed_width ! = - 1 ) {
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2020-03-20 21:51:53 +01:00
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : _remove_item ( Item * p_item , const int p_line , const int p_subitem_line ) {
2017-01-31 22:57:46 +01:00
int size = p_item - > subitems . size ( ) ;
if ( size = = 0 ) {
p_item - > parent - > subitems . erase ( p_item ) ;
2021-05-25 13:48:28 +02:00
// If a newline was erased, all lines AFTER the newline need to be decremented.
2017-01-31 22:57:46 +01:00
if ( p_item - > type = = ITEM_NEWLINE ) {
2021-07-04 00:17:03 +02:00
current_frame - > lines . remove_at ( p_line ) ;
2021-05-25 13:48:28 +02:00
for ( int i = 0 ; i < current - > subitems . size ( ) ; i + + ) {
if ( current - > subitems [ i ] - > line > p_subitem_line ) {
2017-01-31 22:57:46 +01:00
current - > subitems [ i ] - > line - - ;
2020-05-14 16:41:43 +02:00
}
2017-01-31 22:57:46 +01:00
}
}
2017-03-05 16:44:50 +01:00
} else {
2021-05-25 13:48:28 +02:00
// First, remove all child items for the provided item.
2017-01-31 22:57:46 +01:00
for ( int i = 0 ; i < size ; i + + ) {
_remove_item ( p_item - > subitems . front ( ) - > get ( ) , p_line , p_subitem_line ) ;
}
2021-05-25 13:48:28 +02:00
// Then remove the provided item itself.
p_item - > parent - > subitems . erase ( p_item ) ;
2017-01-31 22:57:46 +01:00
}
2021-10-28 12:51:48 +02:00
memdelete ( p_item ) ;
2017-01-31 22:57:46 +01:00
}
2021-11-25 03:58:47 +01:00
void RichTextLabel : : add_image ( const Ref < Texture2D > & p_image , const int p_width , const int p_height , const Color & p_color , InlineAlignment p_alignment ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-05-14 16:41:43 +02:00
if ( current - > type = = ITEM_TABLE ) {
2015-12-26 14:25:17 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2015-12-26 14:25:17 +01:00
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( p_image . is_null ( ) ) ;
2021-02-14 16:02:01 +01:00
ERR_FAIL_COND ( p_image - > get_width ( ) = = 0 ) ;
ERR_FAIL_COND ( p_image - > get_height ( ) = = 0 ) ;
2017-03-05 16:44:50 +01:00
ItemImage * item = memnew ( ItemImage ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
item - > image = p_image ;
2020-05-28 08:17:17 +02:00
item - > color = p_color ;
2021-11-25 03:58:47 +01:00
item - > inline_align = p_alignment ;
2019-10-16 12:00:15 +02:00
if ( p_width > 0 ) {
// custom width
item - > size . width = p_width ;
if ( p_height > 0 ) {
// custom height
item - > size . height = p_height ;
} else {
// calculate height to keep aspect ratio
item - > size . height = p_image - > get_height ( ) * p_width / p_image - > get_width ( ) ;
}
} else {
if ( p_height > 0 ) {
// custom height
item - > size . height = p_height ;
// calculate width to keep aspect ratio
item - > size . width = p_image - > get_width ( ) * p_height / p_image - > get_height ( ) ;
} else {
// keep original width and height
2021-09-25 11:01:45 +02:00
item - > size = p_image - > get_size ( ) ;
2019-10-16 12:00:15 +02:00
}
}
2017-03-05 16:44:50 +01:00
_add_item ( item , false ) ;
2014-02-10 02:10:30 +01:00
}
void RichTextLabel : : add_newline ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-05-14 16:41:43 +02:00
if ( current - > type = = ITEM_TABLE ) {
2015-12-26 14:25:17 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
ItemNewline * item = memnew ( ItemNewline ) ;
item - > line = current_frame - > lines . size ( ) ;
_add_item ( item , false ) ;
2017-11-02 01:46:28 +01:00
current_frame - > lines . resize ( current_frame - > lines . size ( ) + 1 ) ;
_invalidate_current_line ( current_frame ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-01-31 22:57:46 +01:00
bool RichTextLabel : : remove_line ( const int p_line ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
if ( p_line > = ( int ) current_frame - > lines . size ( ) | | p_line < 0 ) {
2017-01-31 22:57:46 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-01-31 22:57:46 +01:00
2021-05-25 13:48:28 +02:00
// Remove all subitems with the same line as that provided.
Vector < int > subitem_indices_to_remove ;
for ( int i = 0 ; i < current - > subitems . size ( ) ; i + + ) {
if ( current - > subitems [ i ] - > line = = p_line ) {
subitem_indices_to_remove . push_back ( i ) ;
}
2018-09-08 20:34:37 +02:00
}
2017-01-31 22:57:46 +01:00
2021-05-25 13:48:28 +02:00
bool had_newline = false ;
// Reverse for loop to remove items from the end first.
for ( int i = subitem_indices_to_remove . size ( ) - 1 ; i > = 0 ; i - - ) {
int subitem_idx = subitem_indices_to_remove [ i ] ;
had_newline = had_newline | | current - > subitems [ subitem_idx ] - > type = = ITEM_NEWLINE ;
_remove_item ( current - > subitems [ subitem_idx ] , current - > subitems [ subitem_idx ] - > line , p_line ) ;
2018-09-08 20:34:37 +02:00
}
2017-01-31 22:57:46 +01:00
2021-05-25 13:48:28 +02:00
if ( ! had_newline ) {
2021-07-04 00:17:03 +02:00
current_frame - > lines . remove_at ( p_line ) ;
2019-12-12 09:35:50 +01:00
if ( current_frame - > lines . size ( ) = = 0 ) {
current_frame - > lines . resize ( 1 ) ;
}
2018-09-08 20:34:37 +02:00
}
2017-01-31 22:57:46 +01:00
2020-05-14 16:41:43 +02:00
if ( p_line = = 0 & & current - > subitems . size ( ) > 0 ) {
2022-05-18 09:17:55 +02:00
main - > lines [ 0 ] . from = main ;
2020-05-14 16:41:43 +02:00
}
2017-01-31 22:57:46 +01:00
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-09-08 20:34:37 +02:00
2017-01-31 22:57:46 +01:00
return true ;
}
2020-11-19 15:45:23 +01:00
void RichTextLabel : : push_dropcap ( const String & p_string , const Ref < Font > & p_font , int p_size , const Rect2 & p_dropcap_margins , const Color & p_color , int p_ol_size , const Color & p_ol_color ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-11-19 15:45:23 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
2020-12-29 09:54:59 +01:00
ERR_FAIL_COND ( p_string . is_empty ( ) ) ;
2020-11-19 15:45:23 +01:00
ERR_FAIL_COND ( p_font . is_null ( ) ) ;
ERR_FAIL_COND ( p_size < = 0 ) ;
ItemDropcap * item = memnew ( ItemDropcap ) ;
item - > text = p_string ;
item - > font = p_font ;
item - > font_size = p_size ;
item - > color = p_color ;
item - > ol_size = p_ol_size ;
item - > ol_color = p_ol_color ;
item - > dropcap_margins = p_dropcap_margins ;
_add_item ( item , false ) ;
}
2022-07-26 09:45:40 +02:00
void RichTextLabel : : push_font ( const Ref < Font > & p_font , int p_size ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( p_font . is_null ( ) ) ;
2017-03-05 16:44:50 +01:00
ItemFont * item = memnew ( ItemFont ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
item - > font = p_font ;
2022-07-26 09:45:40 +02:00
item - > font_size = p_size ;
2017-03-05 16:44:50 +01:00
_add_item ( item , true ) ;
2014-02-10 02:10:30 +01:00
}
2018-09-22 23:41:13 +02:00
2019-10-16 15:22:56 +02:00
void RichTextLabel : : push_normal ( ) {
2022-09-01 17:52:49 +02:00
ERR_FAIL_COND ( theme_cache . normal_font . is_null ( ) ) ;
2019-10-16 15:22:56 +02:00
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . normal_font , theme_cache . normal_font_size ) ;
2019-10-16 15:22:56 +02:00
}
void RichTextLabel : : push_bold ( ) {
2022-09-01 17:52:49 +02:00
ERR_FAIL_COND ( theme_cache . bold_font . is_null ( ) ) ;
2019-10-16 15:22:56 +02:00
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . bold_font , theme_cache . bold_font_size ) ;
2019-10-16 15:22:56 +02:00
}
void RichTextLabel : : push_bold_italics ( ) {
2022-09-01 17:52:49 +02:00
ERR_FAIL_COND ( theme_cache . bold_italics_font . is_null ( ) ) ;
2019-10-16 15:22:56 +02:00
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . bold_italics_font , theme_cache . bold_italics_font_size ) ;
2019-10-16 15:22:56 +02:00
}
void RichTextLabel : : push_italics ( ) {
2022-09-01 17:52:49 +02:00
ERR_FAIL_COND ( theme_cache . italics_font . is_null ( ) ) ;
2019-10-16 15:22:56 +02:00
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . italics_font , theme_cache . italics_font_size ) ;
2019-10-16 15:22:56 +02:00
}
void RichTextLabel : : push_mono ( ) {
2022-09-01 17:52:49 +02:00
ERR_FAIL_COND ( theme_cache . mono_font . is_null ( ) ) ;
2019-10-16 15:22:56 +02:00
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . mono_font , theme_cache . mono_font_size ) ;
2019-10-16 15:22:56 +02:00
}
2020-10-02 14:02:02 +02:00
void RichTextLabel : : push_font_size ( int p_font_size ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemFontSize * item = memnew ( ItemFontSize ) ;
item - > font_size = p_font_size ;
_add_item ( item , true ) ;
}
2022-05-09 11:47:10 +02:00
void RichTextLabel : : push_outline_size ( int p_ol_size ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemOutlineSize * item = memnew ( ItemOutlineSize ) ;
2022-05-09 11:47:10 +02:00
item - > outline_size = p_ol_size ;
2020-10-02 14:02:02 +02:00
_add_item ( item , true ) ;
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : push_color ( const Color & p_color ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemColor * item = memnew ( ItemColor ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
item - > color = p_color ;
_add_item ( item , true ) ;
2014-02-10 02:10:30 +01:00
}
2018-09-22 23:41:13 +02:00
2020-10-02 14:02:02 +02:00
void RichTextLabel : : push_outline_color ( const Color & p_color ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemOutlineColor * item = memnew ( ItemOutlineColor ) ;
item - > color = p_color ;
_add_item ( item , true ) ;
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : push_underline ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemUnderline * item = memnew ( ItemUnderline ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_add_item ( item , true ) ;
2014-02-10 02:10:30 +01:00
}
2018-09-22 23:41:13 +02:00
void RichTextLabel : : push_strikethrough ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-09-22 23:41:13 +02:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemStrikethrough * item = memnew ( ItemStrikethrough ) ;
_add_item ( item , true ) ;
}
2022-04-19 12:27:18 +02:00
void RichTextLabel : : push_paragraph ( HorizontalAlignment p_alignment , Control : : TextDirection p_direction , const String & p_language , TextServer : : StructuredTextParser p_st_parser ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
ItemParagraph * item = memnew ( ItemParagraph ) ;
2021-11-25 03:58:47 +01:00
item - > alignment = p_alignment ;
2020-10-02 14:02:02 +02:00
item - > direction = p_direction ;
item - > language = p_language ;
item - > st_parser = p_st_parser ;
2017-03-05 16:44:50 +01:00
_add_item ( item , true , true ) ;
2014-02-10 02:10:30 +01:00
}
void RichTextLabel : : push_indent ( int p_level ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ERR_FAIL_COND ( p_level < 0 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ItemIndent * item = memnew ( ItemIndent ) ;
item - > level = p_level ;
_add_item ( item , true , true ) ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
void RichTextLabel : : push_list ( int p_level , ListType p_list , bool p_capitalize ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( p_level < 0 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ItemList * item = memnew ( ItemList ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
item - > list_type = p_list ;
2020-10-02 14:02:02 +02:00
item - > level = p_level ;
item - > capitalize = p_capitalize ;
2017-03-05 16:44:50 +01:00
_add_item ( item , true , true ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : push_meta ( const Variant & p_meta ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemMeta * item = memnew ( ItemMeta ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
item - > meta = p_meta ;
_add_item ( item , true ) ;
2014-02-10 02:10:30 +01:00
}
2022-02-21 18:34:16 +01:00
void RichTextLabel : : push_hint ( const String & p_string ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2022-02-21 18:34:16 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemHint * item = memnew ( ItemHint ) ;
item - > description = p_string ;
_add_item ( item , true ) ;
}
2021-11-25 03:58:47 +01:00
void RichTextLabel : : push_table ( int p_columns , InlineAlignment p_alignment ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( p_columns < 1 ) ;
ItemTable * item = memnew ( ItemTable ) ;
2015-12-26 14:25:17 +01:00
item - > columns . resize ( p_columns ) ;
2017-03-05 16:44:50 +01:00
item - > total_width = 0 ;
2021-11-25 03:58:47 +01:00
item - > inline_align = p_alignment ;
2022-05-18 09:17:55 +02:00
for ( int i = 0 ; i < ( int ) item - > columns . size ( ) ; i + + ) {
item - > columns [ i ] . expand = false ;
item - > columns [ i ] . expand_ratio = 1 ;
2015-12-26 14:25:17 +01:00
}
2020-10-02 14:02:02 +02:00
_add_item ( item , true , false ) ;
2015-12-26 14:25:17 +01:00
}
2018-11-10 03:44:58 +01:00
void RichTextLabel : : push_fade ( int p_start_index , int p_length ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-11-10 03:44:58 +01:00
ItemFade * item = memnew ( ItemFade ) ;
item - > starting_index = p_start_index ;
item - > length = p_length ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_shake ( int p_strength = 10 , float p_rate = 24.0f ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-11-10 03:44:58 +01:00
ItemShake * item = memnew ( ItemShake ) ;
item - > strength = p_strength ;
item - > rate = p_rate ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_wave ( float p_frequency = 1.0f , float p_amplitude = 10.0f ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-11-10 03:44:58 +01:00
ItemWave * item = memnew ( ItemWave ) ;
item - > frequency = p_frequency ;
item - > amplitude = p_amplitude ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_tornado ( float p_frequency = 1.0f , float p_radius = 10.0f ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-11-10 03:44:58 +01:00
ItemTornado * item = memnew ( ItemTornado ) ;
item - > frequency = p_frequency ;
item - > radius = p_radius ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_rainbow ( float p_saturation , float p_value , float p_frequency ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-11-10 03:44:58 +01:00
ItemRainbow * item = memnew ( ItemRainbow ) ;
item - > frequency = p_frequency ;
item - > saturation = p_saturation ;
item - > value = p_value ;
_add_item ( item , true ) ;
}
2020-01-26 23:11:09 +01:00
void RichTextLabel : : push_bgcolor ( const Color & p_color ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-01-26 23:11:09 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemBGColor * item = memnew ( ItemBGColor ) ;
item - > color = p_color ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_fgcolor ( const Color & p_color ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-01-26 23:11:09 +01:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemFGColor * item = memnew ( ItemFGColor ) ;
item - > color = p_color ;
_add_item ( item , true ) ;
}
2019-09-25 22:05:42 +02:00
void RichTextLabel : : push_customfx ( Ref < RichTextEffect > p_custom_effect , Dictionary p_environment ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2018-11-10 03:44:58 +01:00
ItemCustomFX * item = memnew ( ItemCustomFX ) ;
2019-09-25 22:05:42 +02:00
item - > custom_effect = p_custom_effect ;
item - > char_fx_transform - > environment = p_environment ;
2018-11-10 03:44:58 +01:00
_add_item ( item , true ) ;
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : set_table_column_expand ( int p_column , bool p_expand , int p_ratio ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type ! = ITEM_TABLE ) ;
2022-05-18 09:17:55 +02:00
2017-03-05 16:44:50 +01:00
ItemTable * table = static_cast < ItemTable * > ( current ) ;
2022-05-18 09:17:55 +02:00
ERR_FAIL_INDEX ( p_column , ( int ) table - > columns . size ( ) ) ;
table - > columns [ p_column ] . expand = p_expand ;
table - > columns [ p_column ] . expand_ratio = p_ratio ;
2015-12-26 14:25:17 +01:00
}
2020-10-02 14:02:02 +02:00
void RichTextLabel : : set_cell_row_background_color ( const Color & p_odd_row_bg , const Color & p_even_row_bg ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
2022-05-18 09:17:55 +02:00
2020-10-02 14:02:02 +02:00
ItemFrame * cell = static_cast < ItemFrame * > ( current ) ;
ERR_FAIL_COND ( ! cell - > cell ) ;
cell - > odd_row_bg = p_odd_row_bg ;
cell - > even_row_bg = p_even_row_bg ;
}
void RichTextLabel : : set_cell_border_color ( const Color & p_color ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
2022-05-18 09:17:55 +02:00
2020-10-02 14:02:02 +02:00
ItemFrame * cell = static_cast < ItemFrame * > ( current ) ;
ERR_FAIL_COND ( ! cell - > cell ) ;
cell - > border = p_color ;
}
void RichTextLabel : : set_cell_size_override ( const Size2 & p_min_size , const Size2 & p_max_size ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
2022-05-18 09:17:55 +02:00
2020-10-02 14:02:02 +02:00
ItemFrame * cell = static_cast < ItemFrame * > ( current ) ;
ERR_FAIL_COND ( ! cell - > cell ) ;
cell - > min_size_over = p_min_size ;
cell - > max_size_over = p_max_size ;
}
void RichTextLabel : : set_cell_padding ( const Rect2 & p_padding ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
2022-05-18 09:17:55 +02:00
2020-10-02 14:02:02 +02:00
ItemFrame * cell = static_cast < ItemFrame * > ( current ) ;
ERR_FAIL_COND ( ! cell - > cell ) ;
cell - > padding = p_padding ;
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : push_cell ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( current - > type ! = ITEM_TABLE ) ;
2015-12-26 14:25:17 +01:00
2017-03-05 16:44:50 +01:00
ItemFrame * item = memnew ( ItemFrame ) ;
item - > parent_frame = current_frame ;
_add_item ( item , true ) ;
current_frame = item ;
item - > cell = true ;
2015-12-26 14:25:17 +01:00
item - > lines . resize ( 1 ) ;
2022-05-18 09:17:55 +02:00
item - > lines [ 0 ] . from = nullptr ;
item - > first_invalid_line . store ( 0 ) ; // parent frame last line ???
2015-12-26 14:25:17 +01:00
}
int RichTextLabel : : get_current_table_column ( ) const {
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( current - > type ! = ITEM_TABLE , - 1 ) ;
2015-12-26 14:25:17 +01:00
2017-03-05 16:44:50 +01:00
ItemTable * table = static_cast < ItemTable * > ( current ) ;
2015-12-26 14:25:17 +01:00
return table - > subitems . size ( ) % table - > columns . size ( ) ;
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : pop ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( ! current - > parent ) ;
2022-05-18 09:17:55 +02:00
2017-03-05 16:44:50 +01:00
if ( current - > type = = ITEM_FRAME ) {
current_frame = static_cast < ItemFrame * > ( current ) - > parent_frame ;
2015-12-26 14:25:17 +01:00
}
2017-03-05 16:44:50 +01:00
current = current - > parent ;
2014-02-10 02:10:30 +01:00
}
void RichTextLabel : : clear ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2014-02-10 02:10:30 +01:00
main - > _clear_children ( ) ;
2017-03-05 16:44:50 +01:00
current = main ;
current_frame = main ;
2015-12-26 14:25:17 +01:00
main - > lines . clear ( ) ;
main - > lines . resize ( 1 ) ;
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ;
2020-10-02 14:02:02 +02:00
selection . click_frame = nullptr ;
selection . click_item = nullptr ;
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2020-10-02 14:02:02 +02:00
2017-03-05 16:44:50 +01:00
current_idx = 1 ;
2020-10-02 14:02:02 +02:00
current_char_ofs = 0 ;
2020-06-14 01:23:32 +02:00
if ( scroll_follow ) {
scroll_following = true ;
}
2020-03-20 21:51:53 +01:00
if ( fixed_width ! = - 1 ) {
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2020-03-20 21:51:53 +01:00
}
2014-02-10 02:10:30 +01:00
}
void RichTextLabel : : set_tab_size ( int p_spaces ) {
2022-03-16 08:50:48 +01:00
if ( tab_size = = p_spaces ) {
return ;
}
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2017-03-05 16:44:50 +01:00
tab_size = p_spaces ;
2022-05-18 09:17:55 +02:00
main - > first_resized_line . store ( 0 ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
int RichTextLabel : : get_tab_size ( ) const {
return tab_size ;
}
2019-11-01 15:17:40 +01:00
void RichTextLabel : : set_fit_content_height ( bool p_enabled ) {
if ( p_enabled ! = fit_content_height ) {
fit_content_height = p_enabled ;
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2019-11-01 15:17:40 +01:00
}
}
bool RichTextLabel : : is_fit_content_height_enabled ( ) const {
return fit_content_height ;
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : set_meta_underline ( bool p_underline ) {
2022-03-16 08:50:48 +01:00
if ( underline_meta = = p_underline ) {
return ;
}
2017-03-05 16:44:50 +01:00
underline_meta = p_underline ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
bool RichTextLabel : : is_meta_underlined ( ) const {
return underline_meta ;
}
2022-02-21 18:34:16 +01:00
void RichTextLabel : : set_hint_underline ( bool p_underline ) {
underline_hint = p_underline ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-02-21 18:34:16 +01:00
}
bool RichTextLabel : : is_hint_underlined ( ) const {
return underline_hint ;
}
2017-09-27 19:24:05 +02:00
void RichTextLabel : : set_override_selected_font_color ( bool p_override_selected_font_color ) {
override_selected_font_color = p_override_selected_font_color ;
}
bool RichTextLabel : : is_overriding_selected_font_color ( ) const {
return override_selected_font_color ;
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : set_offset ( int p_pixel ) {
2017-01-04 05:16:14 +01:00
vscroll - > set_value ( p_pixel ) ;
2014-02-10 02:10:30 +01:00
}
void RichTextLabel : : set_scroll_active ( bool p_active ) {
2020-05-14 16:41:43 +02:00
if ( scroll_active = = p_active ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
scroll_active = p_active ;
2020-02-19 20:12:07 +01:00
vscroll - > set_drag_node_enabled ( p_active ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2014-02-10 02:10:30 +01:00
}
bool RichTextLabel : : is_scroll_active ( ) const {
return scroll_active ;
}
void RichTextLabel : : set_scroll_follow ( bool p_follow ) {
2017-03-05 16:44:50 +01:00
scroll_follow = p_follow ;
2020-05-14 16:41:43 +02:00
if ( ! vscroll - > is_visible_in_tree ( ) | | vscroll - > get_value ( ) > = ( vscroll - > get_max ( ) - vscroll - > get_page ( ) ) ) {
2017-03-05 16:44:50 +01:00
scroll_following = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
bool RichTextLabel : : is_scroll_following ( ) const {
return scroll_follow ;
}
2021-10-23 08:25:39 +02:00
void RichTextLabel : : parse_bbcode ( const String & p_bbcode ) {
2014-02-10 02:10:30 +01:00
clear ( ) ;
2021-10-23 08:25:39 +02:00
append_text ( p_bbcode ) ;
2014-02-10 02:10:30 +01:00
}
2021-10-23 08:25:39 +02:00
void RichTextLabel : : append_text ( const String & p_bbcode ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
MutexLock data_lock ( data_mutex ) ;
2014-02-10 02:10:30 +01:00
int pos = 0 ;
List < String > tag_stack ;
2017-03-05 16:44:50 +01:00
int indent_level = 0 ;
2015-04-21 21:01:58 +02:00
2017-03-05 16:44:50 +01:00
bool in_bold = false ;
bool in_italics = false ;
2022-06-04 16:57:20 +02:00
bool after_list_open_tag = false ;
bool after_list_close_tag = false ;
2015-04-21 21:01:58 +02:00
2018-11-10 03:44:58 +01:00
set_process_internal ( false ) ;
2022-06-04 16:57:20 +02:00
while ( pos < = p_bbcode . length ( ) ) {
2017-03-05 16:44:50 +01:00
int brk_pos = p_bbcode . find ( " [ " , pos ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( brk_pos < 0 ) {
2017-03-05 16:44:50 +01:00
brk_pos = p_bbcode . length ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-06-04 16:57:20 +02:00
String text = brk_pos > pos ? p_bbcode . substr ( pos , brk_pos - pos ) : " " ;
// Trim the first newline character, it may be added later as needed.
if ( after_list_close_tag | | after_list_open_tag ) {
text = text . trim_prefix ( " \n " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( brk_pos = = p_bbcode . length ( ) ) {
2022-06-04 16:57:20 +02:00
// For tags that are not properly closed.
if ( text . is_empty ( ) & & after_list_open_tag ) {
text = " \n " ;
}
if ( ! text . is_empty ( ) ) {
add_text ( text ) ;
}
2018-09-22 23:41:13 +02:00
break ; //nothing else to add
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int brk_end = p_bbcode . find ( " ] " , brk_pos + 1 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( brk_end = = - 1 ) {
2014-02-10 02:10:30 +01:00
//no close, add the rest
2022-06-04 16:57:20 +02:00
text + = p_bbcode . substr ( brk_pos , p_bbcode . length ( ) - brk_pos ) ;
add_text ( text ) ;
2014-02-10 02:10:30 +01:00
break ;
}
2017-03-05 16:44:50 +01:00
String tag = p_bbcode . substr ( brk_pos + 1 , brk_end - brk_pos - 1 ) ;
2020-04-05 17:52:54 +02:00
Vector < String > split_tag_block = tag . split ( " " , false ) ;
2020-05-28 08:17:17 +02:00
// Find optional parameters.
String bbcode_name ;
2022-05-13 15:04:37 +02:00
typedef HashMap < String , String > OptionMap ;
2020-05-28 08:17:17 +02:00
OptionMap bbcode_options ;
2020-12-15 13:04:21 +01:00
if ( ! split_tag_block . is_empty ( ) ) {
2020-05-28 08:17:17 +02:00
bbcode_name = split_tag_block [ 0 ] ;
for ( int i = 1 ; i < split_tag_block . size ( ) ; i + + ) {
const String & expr = split_tag_block [ i ] ;
int value_pos = expr . find ( " = " ) ;
if ( value_pos > - 1 ) {
bbcode_options [ expr . substr ( 0 , value_pos ) ] = expr . substr ( value_pos + 1 ) ;
}
}
} else {
bbcode_name = tag ;
}
// Find main parameter.
String bbcode_value ;
int main_value_pos = bbcode_name . find ( " = " ) ;
if ( main_value_pos > - 1 ) {
bbcode_value = bbcode_name . substr ( main_value_pos + 1 ) ;
bbcode_name = bbcode_name . substr ( 0 , main_value_pos ) ;
}
2015-04-21 21:01:58 +02:00
if ( tag . begins_with ( " / " ) & & tag_stack . size ( ) ) {
2017-03-05 16:44:50 +01:00
bool tag_ok = tag_stack . size ( ) & & tag_stack . front ( ) - > get ( ) = = tag . substr ( 1 , tag . length ( ) ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( tag_stack . front ( ) - > get ( ) = = " b " ) {
2017-03-05 16:44:50 +01:00
in_bold = false ;
2020-05-14 16:41:43 +02:00
}
if ( tag_stack . front ( ) - > get ( ) = = " i " ) {
2017-03-05 16:44:50 +01:00
in_italics = false ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
if ( ( tag_stack . front ( ) - > get ( ) = = " indent " ) | | ( tag_stack . front ( ) - > get ( ) = = " ol " ) | | ( tag_stack . front ( ) - > get ( ) = = " ul " ) ) {
2015-04-21 21:01:58 +02:00
indent_level - - ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
if ( ! tag_ok ) {
2022-06-04 16:57:20 +02:00
text + = " [ " + tag ;
add_text ( text ) ;
after_list_open_tag = false ;
after_list_close_tag = false ;
2018-11-10 03:44:58 +01:00
pos = brk_end ;
2014-02-10 02:10:30 +01:00
continue ;
}
2022-06-04 16:57:20 +02:00
if ( text . is_empty ( ) & & after_list_open_tag ) {
text = " \n " ; // Make empty list have at least one item.
}
after_list_open_tag = false ;
if ( tag = = " /ol " | | tag = = " /ul " ) {
if ( ! text . is_empty ( ) ) {
// Make sure text ends with a newline character, that is, the last item
// will wrap at the end of block.
if ( ! text . ends_with ( " \n " ) ) {
text + = " \n " ;
}
} else if ( ! after_list_close_tag ) {
text = " \n " ; // Make the innermost list item wrap at the end of lists.
}
after_list_close_tag = true ;
} else {
after_list_close_tag = false ;
}
if ( ! text . is_empty ( ) ) {
add_text ( text ) ;
}
2014-02-10 02:10:30 +01:00
tag_stack . pop_front ( ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2020-11-19 15:45:23 +01:00
if ( tag ! = " /img " & & tag ! = " /dropcap " ) {
2014-02-10 02:10:30 +01:00
pop ( ) ;
2020-05-14 16:41:43 +02:00
}
2022-06-04 16:57:20 +02:00
continue ;
}
if ( tag = = " ol " | | tag . begins_with ( " ol " ) | | tag = = " ul " | | tag . begins_with ( " ul " ) ) {
if ( text . is_empty ( ) & & after_list_open_tag ) {
text = " \n " ; // Make each list have at least one item at the beginning.
}
after_list_open_tag = true ;
} else {
after_list_open_tag = false ;
}
if ( ! text . is_empty ( ) ) {
add_text ( text ) ;
}
after_list_close_tag = false ;
2014-02-10 02:10:30 +01:00
2022-06-04 16:57:20 +02:00
if ( tag = = " b " ) {
2014-02-10 02:10:30 +01:00
//use bold font
2017-03-05 16:44:50 +01:00
in_bold = true ;
2020-05-14 16:41:43 +02:00
if ( in_italics ) {
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . bold_italics_font , theme_cache . bold_italics_font_size ) ;
2020-05-14 16:41:43 +02:00
} else {
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . bold_font , theme_cache . bold_font_size ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( tag ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " i " ) {
2014-02-10 02:10:30 +01:00
//use italics font
2017-03-05 16:44:50 +01:00
in_italics = true ;
2020-05-14 16:41:43 +02:00
if ( in_bold ) {
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . bold_italics_font , theme_cache . bold_italics_font_size ) ;
2020-05-14 16:41:43 +02:00
} else {
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . italics_font , theme_cache . italics_font_size ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( tag ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " code " ) {
2014-02-10 02:10:30 +01:00
//use monospace font
2022-09-01 17:52:49 +02:00
push_font ( theme_cache . mono_font , theme_cache . mono_font_size ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( tag ) ;
2015-12-26 14:25:17 +01:00
} else if ( tag . begins_with ( " table= " ) ) {
2020-10-02 14:02:02 +02:00
Vector < String > subtag = tag . substr ( 6 , tag . length ( ) ) . split ( " , " ) ;
int columns = subtag [ 0 ] . to_int ( ) ;
2020-05-14 16:41:43 +02:00
if ( columns < 1 ) {
2017-03-05 16:44:50 +01:00
columns = 1 ;
2020-05-14 16:41:43 +02:00
}
2018-09-22 23:41:13 +02:00
2021-11-25 03:58:47 +01:00
int alignment = INLINE_ALIGNMENT_TOP ;
2021-06-14 09:11:37 +02:00
if ( subtag . size ( ) > 2 ) {
2020-10-02 14:02:02 +02:00
if ( subtag [ 1 ] = = " top " | | subtag [ 1 ] = = " t " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_TOP_TO ;
2020-10-02 14:02:02 +02:00
} else if ( subtag [ 1 ] = = " center " | | subtag [ 1 ] = = " c " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_CENTER_TO ;
2020-10-02 14:02:02 +02:00
} else if ( subtag [ 1 ] = = " bottom " | | subtag [ 1 ] = = " b " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_BOTTOM_TO ;
2021-06-14 09:11:37 +02:00
}
if ( subtag [ 2 ] = = " top " | | subtag [ 2 ] = = " t " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_TOP ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 2 ] = = " center " | | subtag [ 2 ] = = " c " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_CENTER ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 2 ] = = " baseline " | | subtag [ 2 ] = = " l " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_BASELINE ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 2 ] = = " bottom " | | subtag [ 2 ] = = " b " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_BOTTOM ;
2021-06-14 09:11:37 +02:00
}
} else if ( subtag . size ( ) > 1 ) {
if ( subtag [ 1 ] = = " top " | | subtag [ 1 ] = = " t " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_TOP ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 1 ] = = " center " | | subtag [ 1 ] = = " c " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_CENTER ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 1 ] = = " bottom " | | subtag [ 1 ] = = " b " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_BOTTOM ;
2020-10-02 14:02:02 +02:00
}
}
2021-11-25 03:58:47 +01:00
push_table ( columns , ( InlineAlignment ) alignment ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-12-26 14:25:17 +01:00
tag_stack . push_front ( " table " ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " cell " ) {
2015-12-26 14:25:17 +01:00
push_cell ( ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-12-26 14:25:17 +01:00
tag_stack . push_front ( tag ) ;
} else if ( tag . begins_with ( " cell= " ) ) {
2018-03-18 11:28:17 +01:00
int ratio = tag . substr ( 5 , tag . length ( ) ) . to_int ( ) ;
2020-05-14 16:41:43 +02:00
if ( ratio < 1 ) {
2017-03-05 16:44:50 +01:00
ratio = 1 ;
2020-05-14 16:41:43 +02:00
}
2018-09-22 23:41:13 +02:00
2017-03-05 16:44:50 +01:00
set_table_column_expand ( get_current_table_column ( ) , true , ratio ) ;
2015-12-26 14:25:17 +01:00
push_cell ( ) ;
2020-10-02 14:02:02 +02:00
pos = brk_end + 1 ;
tag_stack . push_front ( " cell " ) ;
} else if ( tag . begins_with ( " cell " ) ) {
Vector < String > subtag = tag . substr ( 5 , tag . length ( ) ) . split ( " " ) ;
for ( int i = 0 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " ) ;
if ( subtag_a . size ( ) = = 2 ) {
if ( subtag_a [ 0 ] = = " expand " ) {
int ratio = subtag_a [ 1 ] . to_int ( ) ;
if ( ratio < 1 ) {
ratio = 1 ;
}
set_table_column_expand ( get_current_table_column ( ) , true , ratio ) ;
}
}
}
push_cell ( ) ;
2021-01-08 09:18:12 +01:00
const Color fallback_color = Color ( 0 , 0 , 0 , 0 ) ;
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " ) ;
if ( subtag_a . size ( ) = = 2 ) {
if ( subtag_a [ 0 ] = = " border " ) {
2021-01-08 09:18:12 +01:00
Color color = Color : : from_string ( subtag_a [ 1 ] , fallback_color ) ;
2020-10-02 14:02:02 +02:00
set_cell_border_color ( color ) ;
} else if ( subtag_a [ 0 ] = = " bg " ) {
Vector < String > subtag_b = subtag_a [ 1 ] . split ( " , " ) ;
if ( subtag_b . size ( ) = = 2 ) {
2021-01-08 09:18:12 +01:00
Color color1 = Color : : from_string ( subtag_b [ 0 ] , fallback_color ) ;
Color color2 = Color : : from_string ( subtag_b [ 1 ] , fallback_color ) ;
2020-10-02 14:02:02 +02:00
set_cell_row_background_color ( color1 , color2 ) ;
}
}
}
}
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-12-26 14:25:17 +01:00
tag_stack . push_front ( " cell " ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " u " ) {
2014-02-10 02:10:30 +01:00
//use underline
push_underline ( ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( tag ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " s " ) {
2018-09-22 23:41:13 +02:00
//use strikethrough
push_strikethrough ( ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( tag ) ;
2022-01-07 09:40:13 +01:00
} else if ( tag = = " lb " ) {
add_text ( " [ " ) ;
pos = brk_end + 1 ;
} else if ( tag = = " rb " ) {
add_text ( " ] " ) ;
pos = brk_end + 1 ;
2020-10-02 14:02:02 +02:00
} else if ( tag = = " lrm " ) {
add_text ( String : : chr ( 0x200E ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " rlm " ) {
add_text ( String : : chr ( 0x200F ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " lre " ) {
add_text ( String : : chr ( 0x202A ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " rle " ) {
add_text ( String : : chr ( 0x202B ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " lro " ) {
add_text ( String : : chr ( 0x202D ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " rlo " ) {
add_text ( String : : chr ( 0x202E ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " pdf " ) {
add_text ( String : : chr ( 0x202C ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " alm " ) {
add_text ( String : : chr ( 0x061c ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " lri " ) {
add_text ( String : : chr ( 0x2066 ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " rli " ) {
add_text ( String : : chr ( 0x2027 ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " fsi " ) {
add_text ( String : : chr ( 0x2068 ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " pdi " ) {
add_text ( String : : chr ( 0x2069 ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " zwj " ) {
add_text ( String : : chr ( 0x200D ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " zwnj " ) {
add_text ( String : : chr ( 0x200C ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " wj " ) {
add_text ( String : : chr ( 0x2060 ) ) ;
pos = brk_end + 1 ;
} else if ( tag = = " shy " ) {
add_text ( String : : chr ( 0x00AD ) ) ;
pos = brk_end + 1 ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " center " ) {
2021-11-25 03:58:47 +01:00
push_paragraph ( HORIZONTAL_ALIGNMENT_CENTER ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-04-21 21:01:58 +02:00
tag_stack . push_front ( tag ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " fill " ) {
2021-11-25 03:58:47 +01:00
push_paragraph ( HORIZONTAL_ALIGNMENT_FILL ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-04-21 21:01:58 +02:00
tag_stack . push_front ( tag ) ;
2022-03-08 07:36:19 +01:00
} else if ( tag = = " left " ) {
push_paragraph ( HORIZONTAL_ALIGNMENT_LEFT ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( tag ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " right " ) {
2021-11-25 03:58:47 +01:00
push_paragraph ( HORIZONTAL_ALIGNMENT_RIGHT ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-04-21 21:01:58 +02:00
tag_stack . push_front ( tag ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " ul " ) {
2020-10-02 14:02:02 +02:00
indent_level + + ;
push_list ( indent_level , LIST_DOTS , false ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-04-21 21:01:58 +02:00
tag_stack . push_front ( tag ) ;
2020-10-02 14:02:02 +02:00
} else if ( ( tag = = " ol " ) | | ( tag = = " ol type=1 " ) ) {
indent_level + + ;
push_list ( indent_level , LIST_NUMBERS , false ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2022-01-26 12:52:24 +01:00
tag_stack . push_front ( " ol " ) ;
2020-10-02 14:02:02 +02:00
} else if ( tag = = " ol type=a " ) {
indent_level + + ;
push_list ( indent_level , LIST_LETTERS , false ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " ol " ) ;
} else if ( tag = = " ol type=A " ) {
indent_level + + ;
push_list ( indent_level , LIST_LETTERS , true ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " ol " ) ;
} else if ( tag = = " ol type=i " ) {
indent_level + + ;
push_list ( indent_level , LIST_ROMAN , false ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " ol " ) ;
} else if ( tag = = " ol type=I " ) {
indent_level + + ;
push_list ( indent_level , LIST_ROMAN , true ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " ol " ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " indent " ) {
2015-04-21 21:01:58 +02:00
indent_level + + ;
push_indent ( indent_level ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2015-04-21 21:01:58 +02:00
tag_stack . push_front ( tag ) ;
2020-10-02 14:02:02 +02:00
} else if ( tag = = " p " ) {
2021-11-25 03:58:47 +01:00
push_paragraph ( HORIZONTAL_ALIGNMENT_LEFT ) ;
2020-10-02 14:02:02 +02:00
pos = brk_end + 1 ;
tag_stack . push_front ( " p " ) ;
} else if ( tag . begins_with ( " p " ) ) {
Vector < String > subtag = tag . substr ( 2 , tag . length ( ) ) . split ( " " ) ;
2021-11-25 03:58:47 +01:00
HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT ;
2020-10-02 14:02:02 +02:00
Control : : TextDirection dir = Control : : TEXT_DIRECTION_INHERITED ;
String lang ;
2022-04-19 12:27:18 +02:00
TextServer : : StructuredTextParser st_parser = TextServer : : STRUCTURED_TEXT_DEFAULT ;
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " ) ;
if ( subtag_a . size ( ) = = 2 ) {
if ( subtag_a [ 0 ] = = " align " ) {
if ( subtag_a [ 1 ] = = " l " | | subtag_a [ 1 ] = = " left " ) {
2021-11-25 03:58:47 +01:00
alignment = HORIZONTAL_ALIGNMENT_LEFT ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " c " | | subtag_a [ 1 ] = = " center " ) {
2021-11-25 03:58:47 +01:00
alignment = HORIZONTAL_ALIGNMENT_CENTER ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " r " | | subtag_a [ 1 ] = = " right " ) {
2021-11-25 03:58:47 +01:00
alignment = HORIZONTAL_ALIGNMENT_RIGHT ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " f " | | subtag_a [ 1 ] = = " fill " ) {
2021-11-25 03:58:47 +01:00
alignment = HORIZONTAL_ALIGNMENT_FILL ;
2020-10-02 14:02:02 +02:00
}
} else if ( subtag_a [ 0 ] = = " dir " | | subtag_a [ 0 ] = = " direction " ) {
if ( subtag_a [ 1 ] = = " a " | | subtag_a [ 1 ] = = " auto " ) {
dir = Control : : TEXT_DIRECTION_AUTO ;
} else if ( subtag_a [ 1 ] = = " l " | | subtag_a [ 1 ] = = " ltr " ) {
dir = Control : : TEXT_DIRECTION_LTR ;
} else if ( subtag_a [ 1 ] = = " r " | | subtag_a [ 1 ] = = " rtl " ) {
dir = Control : : TEXT_DIRECTION_RTL ;
}
} else if ( subtag_a [ 0 ] = = " lang " | | subtag_a [ 0 ] = = " language " ) {
lang = subtag_a [ 1 ] ;
} else if ( subtag_a [ 0 ] = = " st " | | subtag_a [ 0 ] = = " bidi_override " ) {
if ( subtag_a [ 1 ] = = " d " | | subtag_a [ 1 ] = = " default " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_DEFAULT ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " u " | | subtag_a [ 1 ] = = " uri " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_URI ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " f " | | subtag_a [ 1 ] = = " file " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_FILE ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " e " | | subtag_a [ 1 ] = = " email " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_EMAIL ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " l " | | subtag_a [ 1 ] = = " list " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_LIST ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " n " | | subtag_a [ 1 ] = = " none " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_NONE ;
2020-10-02 14:02:02 +02:00
} else if ( subtag_a [ 1 ] = = " c " | | subtag_a [ 1 ] = = " custom " ) {
2022-04-19 12:27:18 +02:00
st_parser = TextServer : : STRUCTURED_TEXT_CUSTOM ;
2020-10-02 14:02:02 +02:00
}
}
}
}
2021-11-25 03:58:47 +01:00
push_paragraph ( alignment , dir , lang , st_parser ) ;
2020-10-02 14:02:02 +02:00
pos = brk_end + 1 ;
tag_stack . push_front ( " p " ) ;
2017-03-05 16:44:50 +01:00
} else if ( tag = = " url " ) {
int end = p_bbcode . find ( " [ " , brk_end ) ;
2020-05-14 16:41:43 +02:00
if ( end = = - 1 ) {
2017-03-05 16:44:50 +01:00
end = p_bbcode . length ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
String url = p_bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
2014-02-10 02:10:30 +01:00
push_meta ( url ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( tag ) ;
2015-04-21 21:01:58 +02:00
2014-02-10 02:10:30 +01:00
} else if ( tag . begins_with ( " url= " ) ) {
2017-03-05 16:44:50 +01:00
String url = tag . substr ( 4 , tag . length ( ) ) ;
2014-02-10 02:10:30 +01:00
push_meta ( url ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( " url " ) ;
2022-02-21 18:34:16 +01:00
} else if ( tag . begins_with ( " hint= " ) ) {
String description = tag . substr ( 5 , tag . length ( ) ) ;
push_hint ( description ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " hint " ) ;
2020-11-19 15:45:23 +01:00
} else if ( tag . begins_with ( " dropcap " ) ) {
Vector < String > subtag = tag . substr ( 5 , tag . length ( ) ) . split ( " " ) ;
2022-09-01 17:52:49 +02:00
int fs = theme_cache . normal_font_size * 3 ;
Ref < Font > f = theme_cache . normal_font ;
Color color = theme_cache . default_color ;
Color outline_color = theme_cache . outline_color ;
int outline_size = theme_cache . outline_size ;
2020-11-19 15:45:23 +01:00
Rect2 dropcap_margins = Rect2 ( ) ;
for ( int i = 0 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " ) ;
if ( subtag_a . size ( ) = = 2 ) {
if ( subtag_a [ 0 ] = = " font " | | subtag_a [ 0 ] = = " f " ) {
String fnt = subtag_a [ 1 ] ;
Ref < Font > font = ResourceLoader : : load ( fnt , " Font " ) ;
if ( font . is_valid ( ) ) {
f = font ;
}
} else if ( subtag_a [ 0 ] = = " font_size " ) {
fs = subtag_a [ 1 ] . to_int ( ) ;
} else if ( subtag_a [ 0 ] = = " margins " ) {
Vector < String > subtag_b = subtag_a [ 1 ] . split ( " , " ) ;
if ( subtag_b . size ( ) = = 4 ) {
dropcap_margins . position . x = subtag_b [ 0 ] . to_float ( ) ;
dropcap_margins . position . y = subtag_b [ 1 ] . to_float ( ) ;
dropcap_margins . size . x = subtag_b [ 2 ] . to_float ( ) ;
dropcap_margins . size . y = subtag_b [ 3 ] . to_float ( ) ;
}
} else if ( subtag_a [ 0 ] = = " outline_size " ) {
outline_size = subtag_a [ 1 ] . to_int ( ) ;
} else if ( subtag_a [ 0 ] = = " color " ) {
2021-01-08 09:18:12 +01:00
color = Color : : from_string ( subtag_a [ 1 ] , color ) ;
2020-11-19 15:45:23 +01:00
} else if ( subtag_a [ 0 ] = = " outline_color " ) {
2021-01-08 09:18:12 +01:00
outline_color = Color : : from_string ( subtag_a [ 1 ] , outline_color ) ;
2020-11-19 15:45:23 +01:00
}
}
}
int end = p_bbcode . find ( " [ " , brk_end ) ;
if ( end = = - 1 ) {
end = p_bbcode . length ( ) ;
}
String txt = p_bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
push_dropcap ( txt , f , fs , dropcap_margins , color , outline_size , outline_color ) ;
pos = end ;
tag_stack . push_front ( bbcode_name ) ;
2020-10-02 14:02:02 +02:00
} else if ( tag . begins_with ( " img " ) ) {
2021-11-25 03:58:47 +01:00
int alignment = INLINE_ALIGNMENT_CENTER ;
2020-10-02 14:02:02 +02:00
if ( tag . begins_with ( " img= " ) ) {
2021-06-14 09:11:37 +02:00
Vector < String > subtag = tag . substr ( 4 , tag . length ( ) ) . split ( " , " ) ;
if ( subtag . size ( ) > 1 ) {
if ( subtag [ 0 ] = = " top " | | subtag [ 0 ] = = " t " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_TOP_TO ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 0 ] = = " center " | | subtag [ 0 ] = = " c " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_CENTER_TO ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 0 ] = = " bottom " | | subtag [ 0 ] = = " b " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_BOTTOM_TO ;
2021-06-14 09:11:37 +02:00
}
if ( subtag [ 1 ] = = " top " | | subtag [ 1 ] = = " t " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_TOP ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 1 ] = = " center " | | subtag [ 1 ] = = " c " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_CENTER ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 1 ] = = " baseline " | | subtag [ 1 ] = = " l " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_BASELINE ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 1 ] = = " bottom " | | subtag [ 1 ] = = " b " ) {
2021-11-25 03:58:47 +01:00
alignment | = INLINE_ALIGNMENT_TO_BOTTOM ;
2021-06-14 09:11:37 +02:00
}
} else if ( subtag . size ( ) > 0 ) {
if ( subtag [ 0 ] = = " top " | | subtag [ 0 ] = = " t " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_TOP ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 0 ] = = " center " | | subtag [ 0 ] = = " c " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_CENTER ;
2021-06-14 09:11:37 +02:00
} else if ( subtag [ 0 ] = = " bottom " | | subtag [ 0 ] = = " b " ) {
2021-11-25 03:58:47 +01:00
alignment = INLINE_ALIGNMENT_BOTTOM ;
2021-06-14 09:11:37 +02:00
}
2020-10-02 14:02:02 +02:00
}
}
2017-03-05 16:44:50 +01:00
int end = p_bbcode . find ( " [ " , brk_end ) ;
2020-05-14 16:41:43 +02:00
if ( end = = - 1 ) {
2017-03-05 16:44:50 +01:00
end = p_bbcode . length ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-10-16 12:00:15 +02:00
2017-03-05 16:44:50 +01:00
String image = p_bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
2014-02-10 02:10:30 +01:00
2019-06-11 20:43:37 +02:00
Ref < Texture2D > texture = ResourceLoader : : load ( image , " Texture2D " ) ;
2020-05-14 16:41:43 +02:00
if ( texture . is_valid ( ) ) {
2020-05-28 08:17:17 +02:00
Color color = Color ( 1.0 , 1.0 , 1.0 ) ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator color_option = bbcode_options . find ( " color " ) ;
2020-05-28 08:17:17 +02:00
if ( color_option ) {
2022-05-13 15:04:37 +02:00
color = Color : : from_string ( color_option - > value , color ) ;
2020-05-28 08:17:17 +02:00
}
2019-10-16 12:00:15 +02:00
2020-05-28 08:17:17 +02:00
int width = 0 ;
int height = 0 ;
2020-12-15 13:04:21 +01:00
if ( ! bbcode_value . is_empty ( ) ) {
2020-05-28 08:17:17 +02:00
int sep = bbcode_value . find ( " x " ) ;
if ( sep = = - 1 ) {
width = bbcode_value . to_int ( ) ;
} else {
width = bbcode_value . substr ( 0 , sep ) . to_int ( ) ;
height = bbcode_value . substr ( sep + 1 ) . to_int ( ) ;
}
} else {
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator width_option = bbcode_options . find ( " width " ) ;
2020-05-28 08:17:17 +02:00
if ( width_option ) {
2022-05-13 15:04:37 +02:00
width = width_option - > value . to_int ( ) ;
2020-05-28 08:17:17 +02:00
}
2019-10-16 12:00:15 +02:00
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator height_option = bbcode_options . find ( " height " ) ;
2020-05-28 08:17:17 +02:00
if ( height_option ) {
2022-05-13 15:04:37 +02:00
height = height_option - > value . to_int ( ) ;
2020-05-28 08:17:17 +02:00
}
}
2019-10-16 12:00:15 +02:00
2021-11-25 03:58:47 +01:00
add_image ( texture , width , height , color , ( InlineAlignment ) alignment ) ;
2020-05-14 16:41:43 +02:00
}
2019-10-16 12:00:15 +02:00
pos = end ;
2020-05-28 08:17:17 +02:00
tag_stack . push_front ( bbcode_name ) ;
2014-02-10 02:10:30 +01:00
} else if ( tag . begins_with ( " color= " ) ) {
2020-05-28 08:17:17 +02:00
String color_str = tag . substr ( 6 , tag . length ( ) ) ;
2022-09-01 17:52:49 +02:00
Color color = Color : : from_string ( color_str , theme_cache . default_color ) ;
2014-02-10 02:10:30 +01:00
push_color ( color ) ;
2017-03-05 16:44:50 +01:00
pos = brk_end + 1 ;
2014-02-10 02:10:30 +01:00
tag_stack . push_front ( " color " ) ;
2020-10-02 14:02:02 +02:00
} else if ( tag . begins_with ( " outline_color= " ) ) {
String color_str = tag . substr ( 14 , tag . length ( ) ) ;
2022-09-01 17:52:49 +02:00
Color color = Color : : from_string ( color_str , theme_cache . default_color ) ;
2020-10-02 14:02:02 +02:00
push_outline_color ( color ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " outline_color " ) ;
} else if ( tag . begins_with ( " font_size= " ) ) {
int fnt_size = tag . substr ( 10 , tag . length ( ) ) . to_int ( ) ;
push_font_size ( fnt_size ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " font_size " ) ;
2022-05-09 11:47:10 +02:00
2020-10-02 14:02:02 +02:00
} else if ( tag . begins_with ( " opentype_features= " ) ) {
String fnt_ftr = tag . substr ( 18 , tag . length ( ) ) ;
Vector < String > subtag = fnt_ftr . split ( " , " ) ;
2022-05-09 11:47:10 +02:00
if ( subtag . size ( ) > 0 ) {
2022-09-01 17:52:49 +02:00
Ref < Font > font = theme_cache . normal_font ;
2022-07-26 09:45:40 +02:00
int font_size = 0 ;
ItemFont * font_it = _find_font ( current ) ;
if ( font_it ) {
if ( font_it - > font . is_valid ( ) ) {
font = font_it - > font ;
}
if ( font_it - > font_size > 0 ) {
font_size = font_it - > font_size ;
}
2020-10-02 14:02:02 +02:00
}
2022-05-09 11:47:10 +02:00
Ref < FontVariation > fc ;
fc . instantiate ( ) ;
fc - > set_base_font ( font ) ;
Dictionary features ;
for ( int i = 0 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " ) ;
if ( subtag_a . size ( ) = = 2 ) {
features [ TS - > name_to_tag ( subtag_a [ 0 ] ) ] = subtag_a [ 1 ] . to_int ( ) ;
} else if ( subtag_a . size ( ) = = 1 ) {
features [ TS - > name_to_tag ( subtag_a [ 0 ] ) ] = 1 ;
}
}
fc - > set_opentype_features ( features ) ;
2022-07-26 09:45:40 +02:00
push_font ( fc , font_size ) ;
2020-10-02 14:02:02 +02:00
}
pos = brk_end + 1 ;
tag_stack . push_front ( " opentype_features " ) ;
2022-05-09 11:47:10 +02:00
} else if ( tag . begins_with ( " font= " ) ) {
String fnt = tag . substr ( 5 , tag . length ( ) ) ;
Ref < Font > fc = ResourceLoader : : load ( fnt , " Font " ) ;
if ( fc . is_valid ( ) ) {
push_font ( fc ) ;
}
pos = brk_end + 1 ;
tag_stack . push_front ( " font " ) ;
2020-10-02 14:02:02 +02:00
} else if ( tag . begins_with ( " font " ) ) {
Vector < String > subtag = tag . substr ( 2 , tag . length ( ) ) . split ( " " ) ;
2022-05-09 11:47:10 +02:00
Ref < FontVariation > fc ;
fc . instantiate ( ) ;
2022-07-26 09:45:40 +02:00
int fnt_size = 0 ;
2020-10-02 14:02:02 +02:00
for ( int i = 1 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " , true , 2 ) ;
if ( subtag_a . size ( ) = = 2 ) {
if ( subtag_a [ 0 ] = = " name " | | subtag_a [ 0 ] = = " n " ) {
String fnt = subtag_a [ 1 ] ;
2022-07-07 14:11:08 +02:00
Ref < Font > font_data = ResourceLoader : : load ( fnt , " Font " ) ;
2022-05-09 11:47:10 +02:00
if ( font_data . is_valid ( ) ) {
fc - > set_base_font ( font_data ) ;
2020-10-02 14:02:02 +02:00
}
} else if ( subtag_a [ 0 ] = = " size " | | subtag_a [ 0 ] = = " s " ) {
2022-07-26 09:45:40 +02:00
fnt_size = subtag_a [ 1 ] . to_int ( ) ;
2022-05-09 11:47:10 +02:00
} else if ( subtag_a [ 0 ] = = " glyph_spacing " | | subtag_a [ 0 ] = = " gl " ) {
int spacing = subtag_a [ 1 ] . to_int ( ) ;
fc - > set_spacing ( TextServer : : SPACING_GLYPH , spacing ) ;
} else if ( subtag_a [ 0 ] = = " space_spacing " | | subtag_a [ 0 ] = = " sp " ) {
int spacing = subtag_a [ 1 ] . to_int ( ) ;
fc - > set_spacing ( TextServer : : SPACING_SPACE , spacing ) ;
} else if ( subtag_a [ 0 ] = = " top_spacing " | | subtag_a [ 0 ] = = " top " ) {
int spacing = subtag_a [ 1 ] . to_int ( ) ;
fc - > set_spacing ( TextServer : : SPACING_TOP , spacing ) ;
} else if ( subtag_a [ 0 ] = = " bottom_spacing " | | subtag_a [ 0 ] = = " bt " ) {
int spacing = subtag_a [ 1 ] . to_int ( ) ;
fc - > set_spacing ( TextServer : : SPACING_BOTTOM , spacing ) ;
} else if ( subtag_a [ 0 ] = = " embolden " | | subtag_a [ 0 ] = = " emb " ) {
float emb = subtag_a [ 1 ] . to_float ( ) ;
fc - > set_variation_embolden ( emb ) ;
} else if ( subtag_a [ 0 ] = = " face_index " | | subtag_a [ 0 ] = = " fi " ) {
int fi = subtag_a [ 1 ] . to_int ( ) ;
fc - > set_variation_face_index ( fi ) ;
} else if ( subtag_a [ 0 ] = = " slant " | | subtag_a [ 0 ] = = " sln " ) {
float slant = subtag_a [ 1 ] . to_float ( ) ;
fc - > set_variation_transform ( Transform2D ( 1.0 , slant , 0.0 , 1.0 , 0.0 , 0.0 ) ) ;
} else if ( subtag_a [ 0 ] = = " opentype_variation " | | subtag_a [ 0 ] = = " otv " ) {
Dictionary variations ;
if ( ! subtag_a [ 1 ] . is_empty ( ) ) {
Vector < String > variation_tags = subtag_a [ 1 ] . split ( " , " ) ;
for ( int j = 0 ; j < variation_tags . size ( ) ; j + + ) {
Vector < String > subtag_b = variation_tags [ j ] . split ( " = " ) ;
if ( subtag_b . size ( ) = = 2 ) {
variations [ TS - > name_to_tag ( subtag_b [ 0 ] ) ] = subtag_b [ 1 ] . to_float ( ) ;
}
}
fc - > set_variation_opentype ( variations ) ;
}
} else if ( subtag_a [ 0 ] = = " opentype_features " | | subtag_a [ 0 ] = = " otf " ) {
Dictionary features ;
if ( ! subtag_a [ 1 ] . is_empty ( ) ) {
Vector < String > feature_tags = subtag_a [ 1 ] . split ( " , " ) ;
for ( int j = 0 ; j < feature_tags . size ( ) ; j + + ) {
Vector < String > subtag_b = feature_tags [ j ] . split ( " = " ) ;
if ( subtag_b . size ( ) = = 2 ) {
features [ TS - > name_to_tag ( subtag_b [ 0 ] ) ] = subtag_b [ 1 ] . to_float ( ) ;
} else if ( subtag_b . size ( ) = = 1 ) {
features [ TS - > name_to_tag ( subtag_b [ 0 ] ) ] = 1 ;
}
}
fc - > set_opentype_features ( features ) ;
}
2020-10-02 14:02:02 +02:00
}
}
}
2022-07-26 09:45:40 +02:00
push_font ( fc , fnt_size ) ;
2020-10-02 14:02:02 +02:00
pos = brk_end + 1 ;
tag_stack . push_front ( " font " ) ;
2022-05-09 11:47:10 +02:00
2020-10-02 14:02:02 +02:00
} else if ( tag . begins_with ( " outline_size= " ) ) {
int fnt_size = tag . substr ( 13 , tag . length ( ) ) . to_int ( ) ;
2022-05-09 11:47:10 +02:00
if ( fnt_size > 0 ) {
push_outline_size ( fnt_size ) ;
}
2020-10-02 14:02:02 +02:00
pos = brk_end + 1 ;
tag_stack . push_front ( " outline_size " ) ;
2014-02-10 02:10:30 +01:00
2020-05-28 08:17:17 +02:00
} else if ( bbcode_name = = " fade " ) {
int start_index = 0 ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator start_option = bbcode_options . find ( " start " ) ;
2020-05-28 08:17:17 +02:00
if ( start_option ) {
2022-05-13 15:04:37 +02:00
start_index = start_option - > value . to_int ( ) ;
2020-05-28 08:17:17 +02:00
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
int length = 10 ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator length_option = bbcode_options . find ( " length " ) ;
2020-05-28 08:17:17 +02:00
if ( length_option ) {
2022-05-13 15:04:37 +02:00
length = length_option - > value . to_int ( ) ;
2018-11-10 03:44:58 +01:00
}
2020-05-28 08:17:17 +02:00
push_fade ( start_index , length ) ;
2018-11-10 03:44:58 +01:00
pos = brk_end + 1 ;
tag_stack . push_front ( " fade " ) ;
2020-05-28 08:17:17 +02:00
} else if ( bbcode_name = = " shake " ) {
2018-11-10 03:44:58 +01:00
int strength = 5 ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator strength_option = bbcode_options . find ( " level " ) ;
2020-05-28 08:17:17 +02:00
if ( strength_option ) {
2022-05-13 15:04:37 +02:00
strength = strength_option - > value . to_int ( ) ;
2020-05-28 08:17:17 +02:00
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float rate = 20.0f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator rate_option = bbcode_options . find ( " rate " ) ;
2020-05-28 08:17:17 +02:00
if ( rate_option ) {
2022-05-13 15:04:37 +02:00
rate = rate_option - > value . to_float ( ) ;
2018-11-10 03:44:58 +01:00
}
push_shake ( strength , rate ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " shake " ) ;
set_process_internal ( true ) ;
2020-05-28 08:17:17 +02:00
} else if ( bbcode_name = = " wave " ) {
2018-11-10 03:44:58 +01:00
float amplitude = 20.0f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator amplitude_option = bbcode_options . find ( " amp " ) ;
2020-05-28 08:17:17 +02:00
if ( amplitude_option ) {
2022-05-13 15:04:37 +02:00
amplitude = amplitude_option - > value . to_float ( ) ;
2020-05-28 08:17:17 +02:00
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float period = 5.0f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator period_option = bbcode_options . find ( " freq " ) ;
2020-05-28 08:17:17 +02:00
if ( period_option ) {
2022-05-13 15:04:37 +02:00
period = period_option - > value . to_float ( ) ;
2018-11-10 03:44:58 +01:00
}
2014-02-10 02:10:30 +01:00
2018-11-10 03:44:58 +01:00
push_wave ( period , amplitude ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " wave " ) ;
set_process_internal ( true ) ;
2020-05-28 08:17:17 +02:00
} else if ( bbcode_name = = " tornado " ) {
2018-11-10 03:44:58 +01:00
float radius = 10.0f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator radius_option = bbcode_options . find ( " radius " ) ;
2020-05-28 08:17:17 +02:00
if ( radius_option ) {
2022-05-13 15:04:37 +02:00
radius = radius_option - > value . to_float ( ) ;
2020-05-28 08:17:17 +02:00
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float frequency = 1.0f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator frequency_option = bbcode_options . find ( " freq " ) ;
2020-05-28 08:17:17 +02:00
if ( frequency_option ) {
2022-05-13 15:04:37 +02:00
frequency = frequency_option - > value . to_float ( ) ;
2018-11-10 03:44:58 +01:00
}
push_tornado ( frequency , radius ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " tornado " ) ;
set_process_internal ( true ) ;
2020-05-28 08:17:17 +02:00
} else if ( bbcode_name = = " rainbow " ) {
2018-11-10 03:44:58 +01:00
float saturation = 0.8f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator saturation_option = bbcode_options . find ( " sat " ) ;
2020-05-28 08:17:17 +02:00
if ( saturation_option ) {
2022-05-13 15:04:37 +02:00
saturation = saturation_option - > value . to_float ( ) ;
2020-05-28 08:17:17 +02:00
}
2018-11-10 03:44:58 +01:00
float value = 0.8f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator value_option = bbcode_options . find ( " val " ) ;
2020-05-28 08:17:17 +02:00
if ( value_option ) {
2022-05-13 15:04:37 +02:00
value = value_option - > value . to_float ( ) ;
2020-05-28 08:17:17 +02:00
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float frequency = 1.0f ;
2022-05-13 15:04:37 +02:00
OptionMap : : Iterator frequency_option = bbcode_options . find ( " freq " ) ;
2020-05-28 08:17:17 +02:00
if ( frequency_option ) {
2022-05-13 15:04:37 +02:00
frequency = frequency_option - > value . to_float ( ) ;
2018-11-10 03:44:58 +01:00
}
push_rainbow ( saturation , value , frequency ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " rainbow " ) ;
set_process_internal ( true ) ;
2020-01-26 23:11:09 +01:00
} else if ( tag . begins_with ( " bgcolor= " ) ) {
String color_str = tag . substr ( 8 , tag . length ( ) ) ;
2022-09-01 17:52:49 +02:00
Color color = Color : : from_string ( color_str , theme_cache . default_color ) ;
2020-01-26 23:11:09 +01:00
push_bgcolor ( color ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " bgcolor " ) ;
} else if ( tag . begins_with ( " fgcolor= " ) ) {
String color_str = tag . substr ( 8 , tag . length ( ) ) ;
2022-09-01 17:52:49 +02:00
Color color = Color : : from_string ( color_str , theme_cache . default_color ) ;
2020-01-26 23:11:09 +01:00
push_fgcolor ( color ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " fgcolor " ) ;
2018-11-10 03:44:58 +01:00
} else {
2020-04-05 17:52:54 +02:00
Vector < String > & expr = split_tag_block ;
2018-11-10 03:44:58 +01:00
if ( expr . size ( ) < 1 ) {
add_text ( " [ " ) ;
pos = brk_pos + 1 ;
} else {
String identifier = expr [ 0 ] ;
2021-07-04 00:17:03 +02:00
expr . remove_at ( 0 ) ;
2018-11-10 03:44:58 +01:00
Dictionary properties = parse_expressions_for_values ( expr ) ;
Ref < RichTextEffect > effect = _get_custom_effect_by_code ( identifier ) ;
if ( ! effect . is_null ( ) ) {
2019-09-25 22:05:42 +02:00
push_customfx ( effect , properties ) ;
2018-11-10 03:44:58 +01:00
pos = brk_end + 1 ;
tag_stack . push_front ( identifier ) ;
set_process_internal ( true ) ;
} else {
add_text ( " [ " ) ; //ignore
pos = brk_pos + 1 ;
}
}
2014-02-10 02:10:30 +01:00
}
}
2020-06-12 20:50:39 +02:00
Vector < ItemFX * > fx_items ;
2021-07-16 05:45:57 +02:00
for ( Item * E : main - > subitems ) {
Item * subitem = static_cast < Item * > ( E ) ;
2020-06-12 20:50:39 +02:00
_fetch_item_fx_stack ( subitem , fx_items ) ;
if ( fx_items . size ( ) ) {
set_process_internal ( true ) ;
break ;
}
}
2014-02-10 02:10:30 +01:00
}
2021-01-12 13:03:10 +01:00
void RichTextLabel : : scroll_to_paragraph ( int p_paragraph ) {
2022-08-23 15:27:45 +02:00
_validate_line_caches ( ) ;
2022-05-18 09:17:55 +02:00
if ( p_paragraph < = 0 ) {
vscroll - > set_value ( 0 ) ;
} else if ( p_paragraph > = main - > first_invalid_line . load ( ) ) {
vscroll - > set_value ( vscroll - > get_max ( ) ) ;
} else {
vscroll - > set_value ( main - > lines [ p_paragraph ] . offset . y ) ;
}
2021-01-12 13:03:10 +01:00
}
int RichTextLabel : : get_paragraph_count ( ) const {
return current_frame - > lines . size ( ) ;
}
int RichTextLabel : : get_visible_paragraph_count ( ) const {
if ( ! is_visible ( ) ) {
return 0 ;
}
return visible_paragraph_count ;
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : scroll_to_line ( int p_line ) {
2022-08-23 15:27:45 +02:00
_validate_line_caches ( ) ;
2022-05-18 09:17:55 +02:00
if ( p_line < = 0 ) {
vscroll - > set_value ( 0 ) ;
return ;
}
2021-01-12 13:03:10 +01:00
int line_count = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
MutexLock lock ( main - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2021-01-12 13:03:10 +01:00
if ( ( line_count < = p_line ) & & ( line_count + main - > lines [ i ] . text_buf - > get_line_count ( ) > = p_line ) ) {
float line_offset = 0.f ;
for ( int j = 0 ; j < p_line - line_count ; j + + ) {
2022-09-01 17:52:49 +02:00
line_offset + = main - > lines [ i ] . text_buf - > get_line_size ( j ) . y + theme_cache . line_separation ;
2021-01-12 13:03:10 +01:00
}
vscroll - > set_value ( main - > lines [ i ] . offset . y + line_offset ) ;
return ;
}
line_count + = main - > lines [ i ] . text_buf - > get_line_count ( ) ;
}
2022-05-18 09:17:55 +02:00
vscroll - > set_value ( vscroll - > get_max ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-02-13 18:33:37 +01:00
float RichTextLabel : : get_line_offset ( int p_line ) {
int line_count = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
MutexLock lock ( main - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2022-02-13 18:33:37 +01:00
if ( ( line_count < = p_line ) & & ( p_line < = line_count + main - > lines [ i ] . text_buf - > get_line_count ( ) ) ) {
float line_offset = 0.f ;
for ( int j = 0 ; j < p_line - line_count ; j + + ) {
2022-09-01 17:52:49 +02:00
line_offset + = main - > lines [ i ] . text_buf - > get_line_size ( j ) . y + theme_cache . line_separation ;
2022-02-13 18:33:37 +01:00
}
return main - > lines [ i ] . offset . y + line_offset ;
}
line_count + = main - > lines [ i ] . text_buf - > get_line_count ( ) ;
}
return 0 ;
}
float RichTextLabel : : get_paragraph_offset ( int p_paragraph ) {
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
if ( 0 < = p_paragraph & & p_paragraph < to_line ) {
2022-02-13 18:33:37 +01:00
return main - > lines [ p_paragraph ] . offset . y ;
}
return 0 ;
}
2014-02-10 02:10:30 +01:00
int RichTextLabel : : get_line_count ( ) const {
2021-01-12 13:03:10 +01:00
int line_count = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
MutexLock lock ( main - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2021-01-12 13:03:10 +01:00
line_count + = main - > lines [ i ] . text_buf - > get_line_count ( ) ;
}
return line_count ;
2014-02-10 02:10:30 +01:00
}
2016-10-17 21:03:47 +02:00
int RichTextLabel : : get_visible_line_count ( ) const {
2020-05-14 16:41:43 +02:00
if ( ! is_visible ( ) ) {
2016-10-17 21:03:47 +02:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2016-10-17 21:03:47 +02:00
return visible_line_count ;
}
2014-02-10 02:10:30 +01:00
void RichTextLabel : : set_selection_enabled ( bool p_enabled ) {
2022-03-16 08:50:48 +01:00
if ( selection . enabled = = p_enabled ) {
return ;
}
2017-03-05 16:44:50 +01:00
selection . enabled = p_enabled ;
2014-02-10 02:10:30 +01:00
if ( ! p_enabled ) {
if ( selection . active ) {
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2014-02-10 02:10:30 +01:00
}
2014-02-16 01:16:33 +01:00
set_focus_mode ( FOCUS_NONE ) ;
} else {
set_focus_mode ( FOCUS_ALL ) ;
2014-02-10 02:10:30 +01:00
}
}
2021-10-21 23:02:46 +02:00
void RichTextLabel : : set_deselect_on_focus_loss_enabled ( const bool p_enabled ) {
2022-03-16 08:50:48 +01:00
if ( deselect_on_focus_loss_enabled = = p_enabled ) {
return ;
}
2021-10-21 23:02:46 +02:00
deselect_on_focus_loss_enabled = p_enabled ;
if ( p_enabled & & selection . active & & ! has_focus ( ) ) {
2022-04-04 16:06:57 +02:00
deselect ( ) ;
2021-10-21 23:02:46 +02:00
}
}
2021-11-21 19:26:15 +01:00
Variant RichTextLabel : : get_drag_data ( const Point2 & p_point ) {
if ( selection . drag_attempt & & selection . enabled ) {
String t = get_selected_text ( ) ;
Label * l = memnew ( Label ) ;
l - > set_text ( t ) ;
set_drag_preview ( l ) ;
return t ;
}
return Variant ( ) ;
}
bool RichTextLabel : : _is_click_inside_selection ( ) const {
if ( selection . active & & selection . enabled & & selection . click_frame & & selection . from_frame & & selection . to_frame ) {
const Line & l_click = selection . click_frame - > lines [ selection . click_line ] ;
const Line & l_from = selection . from_frame - > lines [ selection . from_line ] ;
const Line & l_to = selection . to_frame - > lines [ selection . to_line ] ;
return ( l_click . char_offset + selection . click_char > = l_from . char_offset + selection . from_char ) & & ( l_click . char_offset + selection . click_char < = l_to . char_offset + selection . to_char ) ;
} else {
return false ;
}
}
2021-06-16 18:43:34 +02:00
bool RichTextLabel : : _search_table ( ItemTable * p_table , List < Item * > : : Element * p_from , const String & p_string , bool p_reverse_search ) {
List < Item * > : : Element * E = p_from ;
while ( E ! = nullptr ) {
ERR_CONTINUE ( E - > get ( ) - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E - > get ( ) ) ;
if ( p_reverse_search ) {
2022-05-18 09:17:55 +02:00
for ( int i = ( int ) frame - > lines . size ( ) - 1 ; i > = 0 ; i - - ) {
2021-06-16 18:43:34 +02:00
if ( _search_line ( frame , i , p_string , - 1 , p_reverse_search ) ) {
return true ;
}
}
} else {
2022-05-18 09:17:55 +02:00
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
2021-06-16 18:43:34 +02:00
if ( _search_line ( frame , i , p_string , 0 , p_reverse_search ) ) {
return true ;
}
}
}
E = p_reverse_search ? E - > prev ( ) : E - > next ( ) ;
}
return false ;
}
bool RichTextLabel : : _search_line ( ItemFrame * p_frame , int p_line , const String & p_string , int p_char_idx , bool p_reverse_search ) {
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND_V ( p_frame = = nullptr , false ) ;
2022-05-18 09:17:55 +02:00
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = ( int ) p_frame - > lines . size ( ) , false ) ;
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
String text ;
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2020-10-02 14:02:02 +02:00
for ( Item * it = l . from ; it & & it ! = it_to ; it = _get_next_item ( it ) ) {
switch ( it - > type ) {
case ITEM_NEWLINE : {
text + = " \n " ;
} break ;
case ITEM_TEXT : {
2022-04-05 12:40:26 +02:00
ItemText * t = static_cast < ItemText * > ( it ) ;
2020-10-02 14:02:02 +02:00
text + = t - > text ;
} break ;
case ITEM_IMAGE : {
text + = " " ;
} break ;
case ITEM_TABLE : {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
2021-06-16 18:43:34 +02:00
List < Item * > : : Element * E = p_reverse_search ? table - > subitems . back ( ) : table - > subitems . front ( ) ;
if ( _search_table ( table , E , p_string , p_reverse_search ) ) {
return true ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
} break ;
default :
break ;
}
}
2021-06-16 18:43:34 +02:00
int sp = - 1 ;
if ( p_reverse_search ) {
sp = text . rfindn ( p_string , p_char_idx ) ;
} else {
sp = text . findn ( p_string , p_char_idx ) ;
}
2020-10-02 14:02:02 +02:00
if ( sp ! = - 1 ) {
selection . from_frame = p_frame ;
selection . from_line = p_line ;
selection . from_item = _get_item_at_pos ( l . from , it_to , sp ) ;
selection . from_char = sp ;
selection . to_frame = p_frame ;
selection . to_line = p_line ;
2021-06-16 18:43:34 +02:00
selection . to_item = _get_item_at_pos ( l . from , it_to , sp + p_string . length ( ) ) ;
selection . to_char = sp + p_string . length ( ) ;
2020-10-02 14:02:02 +02:00
selection . active = true ;
return true ;
}
return false ;
}
bool RichTextLabel : : search ( const String & p_string , bool p_from_selection , bool p_search_previous ) {
ERR_FAIL_COND_V ( ! selection . enabled , false ) ;
2014-02-10 02:10:30 +01:00
2021-06-16 18:43:34 +02:00
if ( p_string . size ( ) = = 0 ) {
selection . active = false ;
return false ;
}
int char_idx = p_search_previous ? - 1 : 0 ;
int current_line = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
int ending_line = to_line - 1 ;
2020-10-02 14:02:02 +02:00
if ( p_from_selection & & selection . active ) {
2021-06-16 18:43:34 +02:00
// First check to see if other results exist in current line
char_idx = p_search_previous ? selection . from_char - 1 : selection . to_char ;
if ( ! ( p_search_previous & & char_idx < 0 ) & &
_search_line ( selection . from_frame , selection . from_line , p_string , char_idx , p_search_previous ) ) {
scroll_to_line ( selection . from_frame - > line + selection . from_line ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-06-16 18:43:34 +02:00
return true ;
2014-02-10 02:10:30 +01:00
}
2021-06-16 18:43:34 +02:00
char_idx = p_search_previous ? - 1 : 0 ;
// Next, check to see if the current search result is in a table
if ( selection . from_frame - > parent ! = nullptr & & selection . from_frame - > parent - > type = = ITEM_TABLE ) {
// Find last search result in table
ItemTable * parent_table = static_cast < ItemTable * > ( selection . from_frame - > parent ) ;
List < Item * > : : Element * parent_element = p_search_previous ? parent_table - > subitems . back ( ) : parent_table - > subitems . front ( ) ;
while ( parent_element - > get ( ) ! = selection . from_frame ) {
parent_element = p_search_previous ? parent_element - > prev ( ) : parent_element - > next ( ) ;
ERR_FAIL_COND_V ( parent_element = = nullptr , false ) ;
}
// Search remainder of table
if ( ! ( p_search_previous & & parent_element = = parent_table - > subitems . front ( ) ) & &
parent_element ! = parent_table - > subitems . back ( ) ) {
parent_element = p_search_previous ? parent_element - > prev ( ) : parent_element - > next ( ) ; // Don't want to search current item
ERR_FAIL_COND_V ( parent_element = = nullptr , false ) ;
// Search for next element
if ( _search_table ( parent_table , parent_element , p_string , p_search_previous ) ) {
scroll_to_line ( selection . from_frame - > line + selection . from_line ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-06-16 18:43:34 +02:00
return true ;
}
2020-10-02 14:02:02 +02:00
}
2020-05-14 16:41:43 +02:00
}
2021-06-16 18:43:34 +02:00
ending_line = selection . from_frame - > line + selection . from_line ;
current_line = p_search_previous ? ending_line - 1 : ending_line + 1 ;
} else if ( p_search_previous ) {
current_line = ending_line ;
ending_line = 0 ;
2014-02-10 02:10:30 +01:00
}
2021-06-16 18:43:34 +02:00
// Search remainder of the file
while ( current_line ! = ending_line ) {
// Wrap around
if ( current_line < 0 ) {
2022-05-18 09:17:55 +02:00
current_line = to_line - 1 ;
} else if ( current_line > = to_line ) {
2021-06-16 18:43:34 +02:00
current_line = 0 ;
}
if ( _search_line ( main , current_line , p_string , char_idx , p_search_previous ) ) {
scroll_to_line ( current_line ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-06-16 18:43:34 +02:00
return true ;
}
p_search_previous ? current_line - - : current_line + + ;
}
if ( p_from_selection & & selection . active ) {
// Check contents of selection
return _search_line ( main , current_line , p_string , char_idx , p_search_previous ) ;
} else {
return false ;
}
2014-02-10 02:10:30 +01:00
}
2021-02-11 17:33:45 +01:00
String RichTextLabel : : _get_line_text ( ItemFrame * p_frame , int p_line , Selection p_selection ) const {
2014-02-16 01:16:33 +01:00
String text ;
2022-05-18 09:17:55 +02:00
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND_V ( p_frame = = nullptr , text ) ;
2022-05-18 09:17:55 +02:00
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = ( int ) p_frame - > lines . size ( ) , text ) ;
2014-02-16 01:16:33 +01:00
2022-05-18 09:17:55 +02:00
Line & l = p_frame - > lines [ p_line ] ;
2014-02-16 01:16:33 +01:00
2022-05-18 09:17:55 +02:00
Item * it_to = ( p_line + 1 < ( int ) p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2020-10-02 14:02:02 +02:00
int end_idx = 0 ;
if ( it_to ! = nullptr ) {
end_idx = it_to - > index ;
} else {
2022-04-05 12:40:26 +02:00
for ( Item * it = l . from ; it ; it = _get_next_item ( it ) ) {
2020-10-02 14:02:02 +02:00
end_idx = it - > index + 1 ;
}
}
for ( Item * it = l . from ; it & & it ! = it_to ; it = _get_next_item ( it ) ) {
2021-11-10 10:45:21 +01:00
if ( it - > type = = ITEM_TABLE ) {
ItemTable * table = static_cast < ItemTable * > ( it ) ;
for ( Item * E : table - > subitems ) {
ERR_CONTINUE ( E - > type ! = ITEM_FRAME ) ; // Children should all be frames.
ItemFrame * frame = static_cast < ItemFrame * > ( E ) ;
2022-05-18 09:17:55 +02:00
for ( int i = 0 ; i < ( int ) frame - > lines . size ( ) ; i + + ) {
2021-11-10 10:45:21 +01:00
text + = _get_line_text ( frame , i , p_selection ) ;
}
}
}
2020-10-02 14:02:02 +02:00
if ( ( p_selection . to_item ! = nullptr ) & & ( p_selection . to_item - > index < l . from - > index ) ) {
2021-11-10 10:45:21 +01:00
continue ;
2014-02-16 01:16:33 +01:00
}
2020-10-02 14:02:02 +02:00
if ( ( p_selection . from_item ! = nullptr ) & & ( p_selection . from_item - > index > = end_idx ) ) {
2021-11-10 10:45:21 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2021-11-10 10:45:21 +01:00
if ( it - > type = = ITEM_DROPCAP ) {
const ItemDropcap * dc = static_cast < ItemDropcap * > ( it ) ;
text + = dc - > text ;
} else if ( it - > type = = ITEM_TEXT ) {
const ItemText * t = static_cast < ItemText * > ( it ) ;
text + = t - > text ;
} else if ( it - > type = = ITEM_NEWLINE ) {
text + = " \n " ;
} else if ( it - > type = = ITEM_IMAGE ) {
text + = " " ;
2020-10-02 14:02:02 +02:00
}
}
if ( ( l . from ! = nullptr ) & & ( p_frame = = p_selection . to_frame ) & & ( p_selection . to_item ! = nullptr ) & & ( p_selection . to_item - > index > = l . from - > index ) & & ( p_selection . to_item - > index < end_idx ) ) {
text = text . substr ( 0 , p_selection . to_char ) ;
}
if ( ( l . from ! = nullptr ) & & ( p_frame = = p_selection . from_frame ) & & ( p_selection . from_item ! = nullptr ) & & ( p_selection . from_item - > index > = l . from - > index ) & & ( p_selection . from_item - > index < end_idx ) ) {
text = text . substr ( p_selection . from_char , - 1 ) ;
}
return text ;
}
2014-02-16 01:16:33 +01:00
2022-04-12 08:58:04 +02:00
void RichTextLabel : : set_context_menu_enabled ( bool p_enabled ) {
context_menu_enabled = p_enabled ;
}
bool RichTextLabel : : is_context_menu_enabled ( ) const {
return context_menu_enabled ;
}
void RichTextLabel : : set_shortcut_keys_enabled ( bool p_enabled ) {
shortcut_keys_enabled = p_enabled ;
}
bool RichTextLabel : : is_shortcut_keys_enabled ( ) const {
return shortcut_keys_enabled ;
}
// Context menu.
PopupMenu * RichTextLabel : : get_menu ( ) const {
const_cast < RichTextLabel * > ( this ) - > _generate_context_menu ( ) ;
return menu ;
}
bool RichTextLabel : : is_menu_visible ( ) const {
return menu & & menu - > is_visible ( ) ;
}
2021-02-11 17:33:45 +01:00
String RichTextLabel : : get_selected_text ( ) const {
2020-10-02 14:02:02 +02:00
if ( ! selection . active | | ! selection . enabled ) {
return " " ;
2014-02-16 01:16:33 +01:00
}
2020-10-02 14:02:02 +02:00
String text ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
2020-10-02 14:02:02 +02:00
text + = _get_line_text ( main , i , selection ) ;
}
2020-09-14 20:14:27 +02:00
return text ;
}
2022-04-04 16:06:57 +02:00
void RichTextLabel : : deselect ( ) {
selection . active = false ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-04-04 16:06:57 +02:00
}
2020-09-14 20:14:27 +02:00
void RichTextLabel : : selection_copy ( ) {
String text = get_selected_text ( ) ;
2021-12-09 10:42:46 +01:00
if ( ! text . is_empty ( ) ) {
2020-03-03 14:36:29 +01:00
DisplayServer : : get_singleton ( ) - > clipboard_set ( text ) ;
2014-02-16 01:16:33 +01:00
}
}
2022-04-10 18:03:10 +02:00
void RichTextLabel : : select_all ( ) {
if ( ! selection . enabled ) {
return ;
}
Item * it = main ;
Item * from_item = nullptr ;
Item * to_item = nullptr ;
while ( it ) {
if ( it - > type ! = ITEM_FRAME ) {
2022-04-13 14:22:20 +02:00
if ( ! from_item ) {
2022-04-10 18:03:10 +02:00
from_item = it ;
} else {
to_item = it ;
}
}
it = _get_next_item ( it , true ) ;
}
2022-04-13 14:22:20 +02:00
if ( ! from_item | | ! to_item ) {
return ;
}
2022-04-10 18:03:10 +02:00
ItemFrame * from_frame = nullptr ;
int from_line = 0 ;
_find_frame ( from_item , & from_frame , & from_line ) ;
2022-04-13 14:22:20 +02:00
if ( ! from_frame ) {
return ;
}
2022-04-10 18:03:10 +02:00
ItemFrame * to_frame = nullptr ;
int to_line = 0 ;
_find_frame ( to_item , & to_frame , & to_line ) ;
2022-04-13 14:22:20 +02:00
if ( ! to_frame ) {
return ;
}
2022-04-10 18:03:10 +02:00
selection . from_line = from_line ;
selection . from_frame = from_frame ;
selection . from_char = 0 ;
selection . from_item = from_item ;
selection . to_line = to_line ;
selection . to_frame = to_frame ;
selection . to_char = to_frame - > lines [ to_line ] . char_count ;
selection . to_item = to_item ;
selection . active = true ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-04-10 18:03:10 +02:00
}
2014-02-10 02:10:30 +01:00
bool RichTextLabel : : is_selection_enabled ( ) const {
return selection . enabled ;
}
2021-10-21 23:02:46 +02:00
bool RichTextLabel : : is_deselect_on_focus_loss_enabled ( ) const {
return deselect_on_focus_loss_enabled ;
}
2021-02-11 17:33:45 +01:00
int RichTextLabel : : get_selection_from ( ) const {
if ( ! selection . active | | ! selection . enabled ) {
return - 1 ;
}
return selection . from_frame - > lines [ selection . from_line ] . char_offset + selection . from_char ;
}
int RichTextLabel : : get_selection_to ( ) const {
if ( ! selection . active | | ! selection . enabled ) {
return - 1 ;
}
return selection . to_frame - > lines [ selection . to_line ] . char_offset + selection . to_char - 1 ;
}
2020-05-29 16:25:12 +02:00
void RichTextLabel : : set_text ( const String & p_bbcode ) {
2022-03-16 08:50:48 +01:00
if ( text = = p_bbcode ) {
return ;
}
2020-05-29 16:25:12 +02:00
text = p_bbcode ;
2022-02-11 11:29:17 +01:00
if ( use_bbcode ) {
2015-04-21 21:01:58 +02:00
parse_bbcode ( p_bbcode ) ;
2020-05-14 16:41:43 +02:00
} else { // raw text
2016-07-09 14:26:49 +02:00
clear ( ) ;
add_text ( p_bbcode ) ;
}
2015-04-21 21:01:58 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-29 16:25:12 +02:00
String RichTextLabel : : get_text ( ) const {
return text ;
2015-04-21 21:01:58 +02:00
}
void RichTextLabel : : set_use_bbcode ( bool p_enable ) {
2020-05-14 16:41:43 +02:00
if ( use_bbcode = = p_enable ) {
2015-04-21 21:01:58 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
use_bbcode = p_enable ;
2021-03-04 00:51:35 +01:00
notify_property_list_changed ( ) ;
2020-05-29 16:25:12 +02:00
set_text ( text ) ;
2015-04-21 21:01:58 +02:00
}
bool RichTextLabel : : is_using_bbcode ( ) const {
return use_bbcode ;
}
2016-06-29 17:43:51 +02:00
2020-05-29 16:25:12 +02:00
String RichTextLabel : : get_parsed_text ( ) const {
2016-06-29 17:43:51 +02:00
String text = " " ;
Item * it = main ;
while ( it ) {
2021-09-20 08:35:24 +02:00
if ( it - > type = = ITEM_DROPCAP ) {
2022-04-05 12:40:26 +02:00
ItemDropcap * dc = static_cast < ItemDropcap * > ( it ) ;
text + = dc - > text ;
2021-09-20 08:35:24 +02:00
} else if ( it - > type = = ITEM_TEXT ) {
2017-03-05 16:44:50 +01:00
ItemText * t = static_cast < ItemText * > ( it ) ;
2016-06-29 17:43:51 +02:00
text + = t - > text ;
} else if ( it - > type = = ITEM_NEWLINE ) {
text + = " \n " ;
2021-02-11 17:33:45 +01:00
} else if ( it - > type = = ITEM_IMAGE ) {
text + = " " ;
2020-10-02 14:02:02 +02:00
} else if ( it - > type = = ITEM_INDENT | | it - > type = = ITEM_LIST ) {
2016-06-29 17:43:51 +02:00
text + = " \t " ;
}
2017-03-05 16:44:50 +01:00
it = _get_next_item ( it , true ) ;
2016-06-29 17:43:51 +02:00
}
return text ;
}
2020-10-02 14:02:02 +02:00
void RichTextLabel : : set_text_direction ( Control : : TextDirection p_text_direction ) {
ERR_FAIL_COND ( ( int ) p_text_direction < - 1 | | ( int ) p_text_direction > 3 ) ;
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2020-10-02 14:02:02 +02:00
if ( text_direction ! = p_text_direction ) {
text_direction = p_text_direction ;
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
_validate_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-10-02 14:02:02 +02:00
}
}
2017-07-03 21:59:58 +02:00
2022-04-19 12:27:18 +02:00
void RichTextLabel : : set_structured_text_bidi_override ( TextServer : : StructuredTextParser p_parser ) {
2020-10-02 14:02:02 +02:00
if ( st_parser ! = p_parser ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2020-10-02 14:02:02 +02:00
st_parser = p_parser ;
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
_validate_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2017-07-03 21:59:58 +02:00
}
2020-10-02 14:02:02 +02:00
}
2022-04-19 12:27:18 +02:00
TextServer : : StructuredTextParser RichTextLabel : : get_structured_text_bidi_override ( ) const {
2020-10-02 14:02:02 +02:00
return st_parser ;
}
void RichTextLabel : : set_structured_text_bidi_override_options ( Array p_args ) {
2022-05-18 09:17:55 +02:00
if ( st_args ! = p_args ) {
_stop_thread ( ) ;
st_args = p_args ;
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
_validate_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-05-18 09:17:55 +02:00
}
2017-07-03 21:59:58 +02:00
}
2020-10-02 14:02:02 +02:00
Array RichTextLabel : : get_structured_text_bidi_override_options ( ) const {
return st_args ;
}
Control : : TextDirection RichTextLabel : : get_text_direction ( ) const {
return text_direction ;
}
void RichTextLabel : : set_language ( const String & p_language ) {
if ( language ! = p_language ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2020-10-02 14:02:02 +02:00
language = p_language ;
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
_validate_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-10-02 14:02:02 +02:00
}
}
String RichTextLabel : : get_language ( ) const {
return language ;
}
2022-06-15 10:01:45 +02:00
void RichTextLabel : : set_autowrap_mode ( TextServer : : AutowrapMode p_mode ) {
2022-02-03 14:56:44 +01:00
if ( autowrap_mode ! = p_mode ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2022-02-03 14:56:44 +01:00
autowrap_mode = p_mode ;
main - > first_invalid_line = 0 ; //invalidate ALL
2022-05-18 09:17:55 +02:00
_validate_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-02-03 14:56:44 +01:00
}
}
2022-06-15 10:01:45 +02:00
TextServer : : AutowrapMode RichTextLabel : : get_autowrap_mode ( ) const {
2022-02-03 14:56:44 +01:00
return autowrap_mode ;
}
2022-08-20 20:06:13 +02:00
void RichTextLabel : : set_visible_ratio ( float p_ratio ) {
if ( visible_ratio ! = p_ratio ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2022-09-01 19:22:12 +02:00
if ( p_ratio > = 1.0 ) {
2020-10-02 14:02:02 +02:00
visible_characters = - 1 ;
2022-08-20 20:06:13 +02:00
visible_ratio = 1.0 ;
2022-09-01 19:22:12 +02:00
} else if ( p_ratio < 0.0 ) {
2022-08-20 23:17:55 +02:00
visible_characters = 0 ;
2022-08-20 20:06:13 +02:00
visible_ratio = 0.0 ;
2020-10-02 14:02:02 +02:00
} else {
2022-08-20 20:06:13 +02:00
visible_characters = get_total_character_count ( ) * p_ratio ;
visible_ratio = p_ratio ;
2020-10-02 14:02:02 +02:00
}
2022-08-20 23:17:55 +02:00
2022-06-15 10:01:45 +02:00
if ( visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING ) {
2022-08-20 23:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; // Invalidate ALL.
2022-05-18 09:17:55 +02:00
_validate_line_caches ( ) ;
2021-12-05 13:28:32 +01:00
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-10-02 14:02:02 +02:00
}
}
2022-08-20 20:06:13 +02:00
float RichTextLabel : : get_visible_ratio ( ) const {
return visible_ratio ;
2017-07-03 21:59:58 +02:00
}
2021-08-24 22:02:24 +02:00
void RichTextLabel : : set_effects ( Array p_effects ) {
custom_effects = p_effects ;
2021-12-09 10:42:46 +01:00
if ( ( ! text . is_empty ( ) ) & & use_bbcode ) {
2020-05-29 16:25:12 +02:00
parse_bbcode ( text ) ;
2021-03-30 09:20:39 +02:00
}
2018-11-10 03:44:58 +01:00
}
2021-08-24 22:02:24 +02:00
Array RichTextLabel : : get_effects ( ) {
return custom_effects ;
2018-11-10 03:44:58 +01:00
}
void RichTextLabel : : install_effect ( const Variant effect ) {
Ref < RichTextEffect > rteffect ;
rteffect = effect ;
2022-07-24 22:52:50 +02:00
ERR_FAIL_COND_MSG ( rteffect . is_null ( ) , " Invalid RichTextEffect resource. " ) ;
custom_effects . push_back ( effect ) ;
if ( ( ! text . is_empty ( ) ) & & use_bbcode ) {
parse_bbcode ( text ) ;
2018-11-10 03:44:58 +01:00
}
}
2019-11-01 15:17:40 +01:00
int RichTextLabel : : get_content_height ( ) const {
2022-08-23 15:27:45 +02:00
const_cast < RichTextLabel * > ( this ) - > _validate_line_caches ( ) ;
2018-04-11 15:53:13 +02:00
int total_height = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
if ( to_line ) {
MutexLock lock ( main - > lines [ to_line - 1 ] . text_buf - > get_mutex ( ) ) ;
2022-09-01 17:52:49 +02:00
total_height = main - > lines [ to_line - 1 ] . offset . y + main - > lines [ to_line - 1 ] . text_buf - > get_size ( ) . y + main - > lines [ to_line - 1 ] . text_buf - > get_line_count ( ) * theme_cache . line_separation ;
2020-05-14 16:41:43 +02:00
}
2018-04-11 15:53:13 +02:00
return total_height ;
}
2022-02-09 21:02:46 +01:00
int RichTextLabel : : get_content_width ( ) const {
2022-08-23 15:27:45 +02:00
const_cast < RichTextLabel * > ( this ) - > _validate_line_caches ( ) ;
2022-02-09 21:02:46 +01:00
int total_width = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
MutexLock lock ( main - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2022-02-09 21:02:46 +01:00
total_width = MAX ( total_width , main - > lines [ i ] . offset . x + main - > lines [ i ] . text_buf - > get_size ( ) . x ) ;
}
return total_width ;
}
2020-05-29 16:25:12 +02:00
# ifndef DISABLE_DEPRECATED
// People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0)
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 07:03:58 +01:00
// Although some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty.
2020-05-29 16:25:12 +02:00
bool RichTextLabel : : _set ( const StringName & p_name , const Variant & p_value ) {
if ( p_name = = " bbcode_text " & & ! ( ( String ) p_value ) . is_empty ( ) ) {
set_text ( p_value ) ;
return true ;
2021-03-04 00:51:35 +01:00
}
2020-05-29 16:25:12 +02:00
return false ;
2021-03-04 00:51:35 +01:00
}
2020-05-29 16:25:12 +02:00
# endif
2021-03-04 00:51:35 +01:00
2014-02-10 02:10:30 +01:00
void RichTextLabel : : _bind_methods ( ) {
2020-05-29 16:25:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_parsed_text " ) , & RichTextLabel : : get_parsed_text ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " add_text " , " text " ) , & RichTextLabel : : add_text ) ;
2017-07-03 21:59:58 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_text " , " text " ) , & RichTextLabel : : set_text ) ;
2021-11-25 03:58:47 +01:00
ClassDB : : bind_method ( D_METHOD ( " add_image " , " image " , " width " , " height " , " color " , " inline_align " ) , & RichTextLabel : : add_image , DEFVAL ( 0 ) , DEFVAL ( 0 ) , DEFVAL ( Color ( 1.0 , 1.0 , 1.0 ) ) , DEFVAL ( INLINE_ALIGNMENT_CENTER ) ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " newline " ) , & RichTextLabel : : add_newline ) ;
2017-07-22 12:11:42 +02:00
ClassDB : : bind_method ( D_METHOD ( " remove_line " , " line " ) , & RichTextLabel : : remove_line ) ;
2022-07-26 09:45:40 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_font " , " font " , " font_size " ) , & RichTextLabel : : push_font ) ;
2020-10-02 14:02:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_font_size " , " font_size " ) , & RichTextLabel : : push_font_size ) ;
2019-10-16 15:22:56 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_normal " ) , & RichTextLabel : : push_normal ) ;
ClassDB : : bind_method ( D_METHOD ( " push_bold " ) , & RichTextLabel : : push_bold ) ;
ClassDB : : bind_method ( D_METHOD ( " push_bold_italics " ) , & RichTextLabel : : push_bold_italics ) ;
ClassDB : : bind_method ( D_METHOD ( " push_italics " ) , & RichTextLabel : : push_italics ) ;
ClassDB : : bind_method ( D_METHOD ( " push_mono " ) , & RichTextLabel : : push_mono ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_color " , " color " ) , & RichTextLabel : : push_color ) ;
2020-10-02 14:02:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_outline_size " , " outline_size " ) , & RichTextLabel : : push_outline_size ) ;
ClassDB : : bind_method ( D_METHOD ( " push_outline_color " , " color " ) , & RichTextLabel : : push_outline_color ) ;
2022-04-19 12:27:18 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_paragraph " , " alignment " , " base_direction " , " language " , " st_parser " ) , & RichTextLabel : : push_paragraph , DEFVAL ( TextServer : : DIRECTION_AUTO ) , DEFVAL ( " " ) , DEFVAL ( TextServer : : STRUCTURED_TEXT_DEFAULT ) ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_indent " , " level " ) , & RichTextLabel : : push_indent ) ;
2020-10-02 14:02:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_list " , " level " , " type " , " capitalize " ) , & RichTextLabel : : push_list ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_meta " , " data " ) , & RichTextLabel : : push_meta ) ;
2022-02-21 18:34:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_hint " , " description " ) , & RichTextLabel : : push_hint ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_underline " ) , & RichTextLabel : : push_underline ) ;
2018-09-22 23:41:13 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_strikethrough " ) , & RichTextLabel : : push_strikethrough ) ;
2021-11-25 03:58:47 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_table " , " columns " , " inline_align " ) , & RichTextLabel : : push_table , DEFVAL ( INLINE_ALIGNMENT_TOP ) ) ;
2020-11-19 15:45:23 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_dropcap " , " string " , " font " , " size " , " dropcap_margins " , " color " , " outline_size " , " outline_color " ) , & RichTextLabel : : push_dropcap , DEFVAL ( Rect2 ( ) ) , DEFVAL ( Color ( 1 , 1 , 1 ) ) , DEFVAL ( 0 ) , DEFVAL ( Color ( 0 , 0 , 0 , 0 ) ) ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_table_column_expand " , " column " , " expand " , " ratio " ) , & RichTextLabel : : set_table_column_expand ) ;
2020-10-02 14:02:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_cell_row_background_color " , " odd_row_bg " , " even_row_bg " ) , & RichTextLabel : : set_cell_row_background_color ) ;
ClassDB : : bind_method ( D_METHOD ( " set_cell_border_color " , " color " ) , & RichTextLabel : : set_cell_border_color ) ;
ClassDB : : bind_method ( D_METHOD ( " set_cell_size_override " , " min_size " , " max_size " ) , & RichTextLabel : : set_cell_size_override ) ;
ClassDB : : bind_method ( D_METHOD ( " set_cell_padding " , " padding " ) , & RichTextLabel : : set_cell_padding ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_cell " ) , & RichTextLabel : : push_cell ) ;
2020-01-26 23:11:09 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_fgcolor " , " fgcolor " ) , & RichTextLabel : : push_fgcolor ) ;
ClassDB : : bind_method ( D_METHOD ( " push_bgcolor " , " bgcolor " ) , & RichTextLabel : : push_bgcolor ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " pop " ) , & RichTextLabel : : pop ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear " ) , & RichTextLabel : : clear ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_structured_text_bidi_override " , " parser " ) , & RichTextLabel : : set_structured_text_bidi_override ) ;
ClassDB : : bind_method ( D_METHOD ( " get_structured_text_bidi_override " ) , & RichTextLabel : : get_structured_text_bidi_override ) ;
ClassDB : : bind_method ( D_METHOD ( " set_structured_text_bidi_override_options " , " args " ) , & RichTextLabel : : set_structured_text_bidi_override_options ) ;
ClassDB : : bind_method ( D_METHOD ( " get_structured_text_bidi_override_options " ) , & RichTextLabel : : get_structured_text_bidi_override_options ) ;
ClassDB : : bind_method ( D_METHOD ( " set_text_direction " , " direction " ) , & RichTextLabel : : set_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text_direction " ) , & RichTextLabel : : get_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " set_language " , " language " ) , & RichTextLabel : : set_language ) ;
ClassDB : : bind_method ( D_METHOD ( " get_language " ) , & RichTextLabel : : get_language ) ;
2022-02-03 14:56:44 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_autowrap_mode " , " autowrap_mode " ) , & RichTextLabel : : set_autowrap_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_autowrap_mode " ) , & RichTextLabel : : get_autowrap_mode ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_meta_underline " , " enable " ) , & RichTextLabel : : set_meta_underline ) ;
ClassDB : : bind_method ( D_METHOD ( " is_meta_underlined " ) , & RichTextLabel : : is_meta_underlined ) ;
2014-02-10 02:10:30 +01:00
2022-02-21 18:34:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_hint_underline " , " enable " ) , & RichTextLabel : : set_hint_underline ) ;
ClassDB : : bind_method ( D_METHOD ( " is_hint_underlined " ) , & RichTextLabel : : is_hint_underlined ) ;
2017-09-27 19:24:05 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_override_selected_font_color " , " override " ) , & RichTextLabel : : set_override_selected_font_color ) ;
ClassDB : : bind_method ( D_METHOD ( " is_overriding_selected_font_color " ) , & RichTextLabel : : is_overriding_selected_font_color ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_scroll_active " , " active " ) , & RichTextLabel : : set_scroll_active ) ;
ClassDB : : bind_method ( D_METHOD ( " is_scroll_active " ) , & RichTextLabel : : is_scroll_active ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_scroll_follow " , " follow " ) , & RichTextLabel : : set_scroll_follow ) ;
ClassDB : : bind_method ( D_METHOD ( " is_scroll_following " ) , & RichTextLabel : : is_scroll_following ) ;
2015-05-18 17:45:53 +02:00
2021-11-30 17:46:36 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll_bar " ) , & RichTextLabel : : get_v_scroll_bar ) ;
2015-11-28 00:11:49 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " scroll_to_line " , " line " ) , & RichTextLabel : : scroll_to_line ) ;
2021-01-12 13:03:10 +01:00
ClassDB : : bind_method ( D_METHOD ( " scroll_to_paragraph " , " paragraph " ) , & RichTextLabel : : scroll_to_paragraph ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_tab_size " , " spaces " ) , & RichTextLabel : : set_tab_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_tab_size " ) , & RichTextLabel : : get_tab_size ) ;
2014-02-10 02:10:30 +01:00
2019-11-01 15:17:40 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_fit_content_height " , " enabled " ) , & RichTextLabel : : set_fit_content_height ) ;
ClassDB : : bind_method ( D_METHOD ( " is_fit_content_height_enabled " ) , & RichTextLabel : : is_fit_content_height_enabled ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_selection_enabled " , " enabled " ) , & RichTextLabel : : set_selection_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_selection_enabled " ) , & RichTextLabel : : is_selection_enabled ) ;
2014-05-14 06:22:15 +02:00
2022-04-12 08:58:04 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_context_menu_enabled " , " enabled " ) , & RichTextLabel : : set_context_menu_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_context_menu_enabled " ) , & RichTextLabel : : is_context_menu_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_shortcut_keys_enabled " , " enabled " ) , & RichTextLabel : : set_shortcut_keys_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_shortcut_keys_enabled " ) , & RichTextLabel : : is_shortcut_keys_enabled ) ;
2021-10-21 23:02:46 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_deselect_on_focus_loss_enabled " , " enable " ) , & RichTextLabel : : set_deselect_on_focus_loss_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_deselect_on_focus_loss_enabled " ) , & RichTextLabel : : is_deselect_on_focus_loss_enabled ) ;
2021-02-11 17:33:45 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_selection_from " ) , & RichTextLabel : : get_selection_from ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_to " ) , & RichTextLabel : : get_selection_to ) ;
2022-04-10 18:03:10 +02:00
ClassDB : : bind_method ( D_METHOD ( " select_all " ) , & RichTextLabel : : select_all ) ;
2021-02-11 17:33:45 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_selected_text " ) , & RichTextLabel : : get_selected_text ) ;
2022-04-04 16:06:57 +02:00
ClassDB : : bind_method ( D_METHOD ( " deselect " ) , & RichTextLabel : : deselect ) ;
2021-02-11 17:33:45 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " parse_bbcode " , " bbcode " ) , & RichTextLabel : : parse_bbcode ) ;
2020-05-29 16:25:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " append_text " , " bbcode " ) , & RichTextLabel : : append_text ) ;
2015-04-21 21:01:58 +02:00
2020-05-29 16:25:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_text " ) , & RichTextLabel : : get_text ) ;
2015-06-02 00:42:34 +02:00
2022-05-18 09:17:55 +02:00
ClassDB : : bind_method ( D_METHOD ( " is_ready " ) , & RichTextLabel : : is_ready ) ;
ClassDB : : bind_method ( D_METHOD ( " set_threaded " , " threaded " ) , & RichTextLabel : : set_threaded ) ;
ClassDB : : bind_method ( D_METHOD ( " is_threaded " ) , & RichTextLabel : : is_threaded ) ;
ClassDB : : bind_method ( D_METHOD ( " set_progress_bar_delay " , " delay_ms " ) , & RichTextLabel : : set_progress_bar_delay ) ;
ClassDB : : bind_method ( D_METHOD ( " get_progress_bar_delay " ) , & RichTextLabel : : get_progress_bar_delay ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_visible_characters " , " amount " ) , & RichTextLabel : : set_visible_characters ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_characters " ) , & RichTextLabel : : get_visible_characters ) ;
2015-06-02 00:42:34 +02:00
2021-12-05 13:28:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_visible_characters_behavior " ) , & RichTextLabel : : get_visible_characters_behavior ) ;
ClassDB : : bind_method ( D_METHOD ( " set_visible_characters_behavior " , " behavior " ) , & RichTextLabel : : set_visible_characters_behavior ) ;
2022-08-20 20:06:13 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_visible_ratio " , " ratio " ) , & RichTextLabel : : set_visible_ratio ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_ratio " ) , & RichTextLabel : : get_visible_ratio ) ;
2017-07-03 21:59:58 +02:00
2022-02-11 22:02:59 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_character_line " , " character " ) , & RichTextLabel : : get_character_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_character_paragraph " , " character " ) , & RichTextLabel : : get_character_paragraph ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_total_character_count " ) , & RichTextLabel : : get_total_character_count ) ;
2015-04-21 21:01:58 +02:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_use_bbcode " , " enable " ) , & RichTextLabel : : set_use_bbcode ) ;
ClassDB : : bind_method ( D_METHOD ( " is_using_bbcode " ) , & RichTextLabel : : is_using_bbcode ) ;
2014-05-14 06:22:15 +02:00
2016-10-17 21:03:47 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_line_count " ) , & RichTextLabel : : get_line_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_line_count " ) , & RichTextLabel : : get_visible_line_count ) ;
2021-01-12 13:03:10 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_paragraph_count " ) , & RichTextLabel : : get_paragraph_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_paragraph_count " ) , & RichTextLabel : : get_visible_paragraph_count ) ;
2018-04-11 15:53:13 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_content_height " ) , & RichTextLabel : : get_content_height ) ;
2022-02-09 21:02:46 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_content_width " ) , & RichTextLabel : : get_content_width ) ;
2018-04-11 15:53:13 +02:00
2022-02-13 18:33:37 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_line_offset " , " line " ) , & RichTextLabel : : get_line_offset ) ;
ClassDB : : bind_method ( D_METHOD ( " get_paragraph_offset " , " paragraph " ) , & RichTextLabel : : get_paragraph_offset ) ;
2018-11-10 03:44:58 +01:00
ClassDB : : bind_method ( D_METHOD ( " parse_expressions_for_values " , " expressions " ) , & RichTextLabel : : parse_expressions_for_values ) ;
ClassDB : : bind_method ( D_METHOD ( " set_effects " , " effects " ) , & RichTextLabel : : set_effects ) ;
ClassDB : : bind_method ( D_METHOD ( " get_effects " ) , & RichTextLabel : : get_effects ) ;
ClassDB : : bind_method ( D_METHOD ( " install_effect " , " effect " ) , & RichTextLabel : : install_effect ) ;
2022-04-12 08:58:04 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_menu " ) , & RichTextLabel : : get_menu ) ;
ClassDB : : bind_method ( D_METHOD ( " is_menu_visible " ) , & RichTextLabel : : is_menu_visible ) ;
2022-03-31 14:06:10 +02:00
// Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets.
2022-02-11 11:29:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " bbcode_enabled " ) , " set_use_bbcode " , " is_using_bbcode " ) ;
2018-09-27 09:09:41 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2022-08-27 20:18:08 +02:00
2019-11-01 15:17:40 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " fit_content_height " ) , " set_fit_content_height " , " is_fit_content_height_enabled " ) ;
2018-01-11 23:35:12 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_active " ) , " set_scroll_active " , " is_scroll_active " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_following " ) , " set_scroll_follow " , " is_scroll_following " ) ;
2022-08-27 20:18:08 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " autowrap_mode " , PROPERTY_HINT_ENUM , " Off,Arbitrary,Word,Word (Smart) " ) , " set_autowrap_mode " , " get_autowrap_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " tab_size " , PROPERTY_HINT_RANGE , " 0,24,1 " ) , " set_tab_size " , " get_tab_size " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " context_menu_enabled " ) , " set_context_menu_enabled " , " is_context_menu_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " shortcut_keys_enabled " ) , " set_shortcut_keys_enabled " , " is_shortcut_keys_enabled " ) ;
ADD_GROUP ( " Markup " , " " ) ;
2021-06-25 14:55:26 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " custom_effects " , PROPERTY_HINT_ARRAY_TYPE , vformat ( " %s/%s:%s " , Variant : : OBJECT , PROPERTY_HINT_RESOURCE_TYPE , " RichTextEffect " ) , ( PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE ) ) , " set_effects " , " get_effects " ) ;
2022-02-11 11:29:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " meta_underlined " ) , " set_meta_underline " , " is_meta_underlined " ) ;
2022-02-21 18:34:16 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " hint_underlined " ) , " set_hint_underline " , " is_hint_underlined " ) ;
2022-02-11 11:29:17 +01:00
2022-08-27 20:18:08 +02:00
ADD_GROUP ( " Threading " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " threaded " ) , " set_threaded " , " is_threaded " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " progress_bar_delay " , PROPERTY_HINT_NONE , " suffix:ms " ) , " set_progress_bar_delay " , " get_progress_bar_delay " ) ;
ADD_GROUP ( " Text Selection " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " selection_enabled " ) , " set_selection_enabled " , " is_selection_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " override_selected_font_color " ) , " set_override_selected_font_color " , " is_overriding_selected_font_color " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " deselect_on_focus_loss_enabled " ) , " set_deselect_on_focus_loss_enabled " , " is_deselect_on_focus_loss_enabled " ) ;
ADD_GROUP ( " Displayed Text " , " " ) ;
2022-08-20 20:06:13 +02:00
// Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied.
2022-02-11 11:29:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " visible_characters " , PROPERTY_HINT_RANGE , " -1,128000,1 " ) , " set_visible_characters " , " get_visible_characters " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " visible_characters_behavior " , PROPERTY_HINT_ENUM , " Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left) " ) , " set_visible_characters_behavior " , " get_visible_characters_behavior " ) ;
2022-08-20 20:06:13 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " visible_ratio " , PROPERTY_HINT_RANGE , " 0,1,0.001 " ) , " set_visible_ratio " , " get_visible_ratio " ) ;
2018-11-10 03:44:58 +01:00
2022-05-09 11:47:10 +02:00
ADD_GROUP ( " BiDi " , " " ) ;
2021-05-22 04:30:58 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " text_direction " , PROPERTY_HINT_ENUM , " Auto,Left-to-Right,Right-to-Left,Inherited " ) , " set_text_direction " , " get_text_direction " ) ;
2021-09-23 13:08:50 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " language " , PROPERTY_HINT_LOCALE_ID , " " ) , " set_language " , " get_language " ) ;
2020-10-02 14:02:02 +02: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 " ) ;
2017-10-05 13:44:00 +02:00
ADD_SIGNAL ( MethodInfo ( " meta_clicked " , PropertyInfo ( Variant : : NIL , " meta " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
2017-11-17 06:49:39 +01:00
ADD_SIGNAL ( MethodInfo ( " meta_hover_started " , PropertyInfo ( Variant : : NIL , " meta " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " meta_hover_ended " , PropertyInfo ( Variant : : NIL , " meta " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
2014-02-10 02:10:30 +01:00
2022-05-18 09:17:55 +02:00
ADD_SIGNAL ( MethodInfo ( " finished " ) ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( LIST_NUMBERS ) ;
BIND_ENUM_CONSTANT ( LIST_LETTERS ) ;
2020-10-02 14:02:02 +02:00
BIND_ENUM_CONSTANT ( LIST_ROMAN ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( LIST_DOTS ) ;
BIND_ENUM_CONSTANT ( ITEM_FRAME ) ;
BIND_ENUM_CONSTANT ( ITEM_TEXT ) ;
BIND_ENUM_CONSTANT ( ITEM_IMAGE ) ;
BIND_ENUM_CONSTANT ( ITEM_NEWLINE ) ;
BIND_ENUM_CONSTANT ( ITEM_FONT ) ;
2020-10-02 14:02:02 +02:00
BIND_ENUM_CONSTANT ( ITEM_FONT_SIZE ) ;
BIND_ENUM_CONSTANT ( ITEM_FONT_FEATURES ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ITEM_COLOR ) ;
2020-10-02 14:02:02 +02:00
BIND_ENUM_CONSTANT ( ITEM_OUTLINE_SIZE ) ;
BIND_ENUM_CONSTANT ( ITEM_OUTLINE_COLOR ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ITEM_UNDERLINE ) ;
2018-09-22 23:41:13 +02:00
BIND_ENUM_CONSTANT ( ITEM_STRIKETHROUGH ) ;
2020-10-02 14:02:02 +02:00
BIND_ENUM_CONSTANT ( ITEM_PARAGRAPH ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ITEM_INDENT ) ;
BIND_ENUM_CONSTANT ( ITEM_LIST ) ;
2017-10-21 20:58:02 +02:00
BIND_ENUM_CONSTANT ( ITEM_TABLE ) ;
2018-11-10 03:44:58 +01:00
BIND_ENUM_CONSTANT ( ITEM_FADE ) ;
BIND_ENUM_CONSTANT ( ITEM_SHAKE ) ;
BIND_ENUM_CONSTANT ( ITEM_WAVE ) ;
BIND_ENUM_CONSTANT ( ITEM_TORNADO ) ;
BIND_ENUM_CONSTANT ( ITEM_RAINBOW ) ;
2020-01-26 23:11:09 +01:00
BIND_ENUM_CONSTANT ( ITEM_BGCOLOR ) ;
BIND_ENUM_CONSTANT ( ITEM_FGCOLOR ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ITEM_META ) ;
2022-02-21 18:34:16 +01:00
BIND_ENUM_CONSTANT ( ITEM_HINT ) ;
2020-11-19 15:45:23 +01:00
BIND_ENUM_CONSTANT ( ITEM_DROPCAP ) ;
BIND_ENUM_CONSTANT ( ITEM_CUSTOMFX ) ;
2021-12-05 13:28:32 +01:00
}
2022-06-15 10:01:45 +02:00
TextServer : : VisibleCharactersBehavior RichTextLabel : : get_visible_characters_behavior ( ) const {
2021-12-05 13:28:32 +01:00
return visible_chars_behavior ;
}
2022-06-15 10:01:45 +02:00
void RichTextLabel : : set_visible_characters_behavior ( TextServer : : VisibleCharactersBehavior p_behavior ) {
2021-12-05 13:28:32 +01:00
if ( visible_chars_behavior ! = p_behavior ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2021-12-05 13:28:32 +01:00
visible_chars_behavior = p_behavior ;
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
_validate_line_caches ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-12-05 13:28:32 +01:00
}
2014-02-10 02:10:30 +01:00
}
2015-06-02 00:42:34 +02:00
void RichTextLabel : : set_visible_characters ( int p_visible ) {
2021-09-20 08:35:24 +02:00
if ( visible_characters ! = p_visible ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2021-09-20 08:35:24 +02:00
visible_characters = p_visible ;
if ( p_visible = = - 1 ) {
2022-08-20 20:06:13 +02:00
visible_ratio = 1 ;
2021-09-20 08:35:24 +02:00
} else {
int total_char_count = get_total_character_count ( ) ;
if ( total_char_count > 0 ) {
2022-08-20 20:06:13 +02:00
visible_ratio = ( float ) p_visible / ( float ) total_char_count ;
2021-09-20 08:35:24 +02:00
}
2021-01-02 11:46:44 +01:00
}
2022-06-15 10:01:45 +02:00
if ( visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING ) {
2022-05-18 09:17:55 +02:00
main - > first_invalid_line . store ( 0 ) ; //invalidate ALL
_validate_line_caches ( ) ;
2021-12-05 13:28:32 +01:00
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-01-02 11:46:44 +01:00
}
2015-06-02 00:42:34 +02:00
}
int RichTextLabel : : get_visible_characters ( ) const {
return visible_characters ;
}
2020-05-14 14:29:06 +02:00
2022-02-11 22:02:59 +01:00
int RichTextLabel : : get_character_line ( int p_char ) {
int line_count = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
MutexLock lock ( main - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2022-02-11 22:02:59 +01:00
if ( main - > lines [ i ] . char_offset < p_char & & p_char < = main - > lines [ i ] . char_offset + main - > lines [ i ] . char_count ) {
for ( int j = 0 ; j < main - > lines [ i ] . text_buf - > get_line_count ( ) ; j + + ) {
Vector2i range = main - > lines [ i ] . text_buf - > get_line_range ( j ) ;
if ( main - > lines [ i ] . char_offset + range . x < p_char & & p_char < = main - > lines [ i ] . char_offset + range . y ) {
return line_count ;
}
line_count + + ;
}
} else {
line_count + = main - > lines [ i ] . text_buf - > get_line_count ( ) ;
}
}
return - 1 ;
}
int RichTextLabel : : get_character_paragraph ( int p_char ) {
int para_count = 0 ;
2022-05-18 09:17:55 +02:00
int to_line = main - > first_invalid_line . load ( ) ;
for ( int i = 0 ; i < to_line ; i + + ) {
2022-02-11 22:02:59 +01:00
if ( main - > lines [ i ] . char_offset < p_char & & p_char < = main - > lines [ i ] . char_offset + main - > lines [ i ] . char_count ) {
return para_count ;
} else {
para_count + + ;
}
}
return - 1 ;
}
2015-06-02 00:42:34 +02:00
int RichTextLabel : : get_total_character_count ( ) const {
2021-09-20 08:35:24 +02:00
// Note: Do not use line buffer "char_count", it includes only visible characters.
2017-03-05 16:44:50 +01:00
int tc = 0 ;
2021-09-20 08:35:24 +02:00
Item * it = main ;
while ( it ) {
if ( it - > type = = ITEM_TEXT ) {
ItemText * t = static_cast < ItemText * > ( it ) ;
tc + = t - > text . length ( ) ;
} else if ( it - > type = = ITEM_NEWLINE ) {
tc + + ;
} else if ( it - > type = = ITEM_IMAGE ) {
tc + + ;
}
it = _get_next_item ( it , true ) ;
2020-05-14 16:41:43 +02:00
}
2015-06-02 00:42:34 +02:00
return tc ;
}
2021-12-05 13:28:32 +01:00
int RichTextLabel : : get_total_glyph_count ( ) const {
int tg = 0 ;
Item * it = main ;
while ( it ) {
if ( it - > type = = ITEM_FRAME ) {
ItemFrame * f = static_cast < ItemFrame * > ( it ) ;
2022-05-18 09:17:55 +02:00
for ( int i = 0 ; i < ( int ) f - > lines . size ( ) ; i + + ) {
MutexLock lock ( f - > lines [ i ] . text_buf - > get_mutex ( ) ) ;
2021-12-05 13:28:32 +01:00
tg + = TS - > shaped_text_get_glyph_count ( f - > lines [ i ] . text_buf - > get_rid ( ) ) ;
}
}
it = _get_next_item ( it , true ) ;
}
return tg ;
}
2018-07-20 23:14:33 +02:00
void RichTextLabel : : set_fixed_size_to_width ( int p_width ) {
2022-03-16 08:50:48 +01:00
if ( fixed_width = = p_width ) {
return ;
}
2018-07-20 23:14:33 +02:00
fixed_width = p_width ;
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2018-07-20 23:14:33 +02:00
}
Size2 RichTextLabel : : get_minimum_size ( ) const {
2022-09-01 17:52:49 +02:00
Size2 size = theme_cache . normal_style - > get_minimum_size ( ) ;
2021-03-29 16:26:53 +02:00
2018-07-20 23:14:33 +02:00
if ( fixed_width ! = - 1 ) {
2021-03-29 16:26:53 +02:00
size . x + = fixed_width ;
2019-11-01 15:17:40 +01:00
}
2022-01-27 11:05:21 +01:00
if ( fit_content_height ) {
2021-03-29 16:26:53 +02:00
size . y + = get_content_height ( ) ;
2018-07-20 23:14:33 +02:00
}
2019-11-01 15:17:40 +01:00
return size ;
2018-07-20 23:14:33 +02:00
}
2022-04-12 08:58:04 +02:00
// Context menu.
void RichTextLabel : : _generate_context_menu ( ) {
if ( ! menu ) {
menu = memnew ( PopupMenu ) ;
add_child ( menu , false , INTERNAL_MODE_FRONT ) ;
menu - > connect ( " id_pressed " , callable_mp ( this , & RichTextLabel : : _menu_option ) ) ;
}
// Reorganize context menu.
menu - > clear ( ) ;
if ( selection . enabled ) {
menu - > add_item ( RTR ( " Copy " ) , MENU_COPY , is_shortcut_keys_enabled ( ) ? _get_menu_action_accelerator ( " ui_copy " ) : Key : : NONE ) ;
menu - > add_item ( RTR ( " Select All " ) , MENU_SELECT_ALL , is_shortcut_keys_enabled ( ) ? _get_menu_action_accelerator ( " ui_text_select_all " ) : Key : : NONE ) ;
}
}
Key RichTextLabel : : _get_menu_action_accelerator ( const String & p_action ) {
const List < Ref < InputEvent > > * events = InputMap : : get_singleton ( ) - > action_get_events ( p_action ) ;
if ( ! events ) {
return Key : : NONE ;
}
// Use first event in the list for the accelerator.
const List < Ref < InputEvent > > : : Element * first_event = events - > front ( ) ;
if ( ! first_event ) {
return Key : : NONE ;
}
const Ref < InputEventKey > event = first_event - > get ( ) ;
if ( event . is_null ( ) ) {
return Key : : NONE ;
}
// Use physical keycode if non-zero
if ( event - > get_physical_keycode ( ) ! = Key : : NONE ) {
return event - > get_physical_keycode_with_modifiers ( ) ;
} else {
return event - > get_keycode_with_modifiers ( ) ;
}
}
void RichTextLabel : : _menu_option ( int p_option ) {
switch ( p_option ) {
case MENU_COPY : {
selection_copy ( ) ;
} break ;
case MENU_SELECT_ALL : {
select_all ( ) ;
} break ;
}
}
2020-01-26 23:11:09 +01:00
void RichTextLabel : : _draw_fbg_boxes ( RID p_ci , RID p_rid , Vector2 line_off , Item * it_from , Item * it_to , int start , int end , int fbg_flag ) {
Vector2i fbg_index = Vector2i ( end , start ) ;
Color last_color = Color ( 0 , 0 , 0 , 0 ) ;
bool draw_box = false ;
// Draw a box based on color tags associated with glyphs
for ( int i = start ; i < end ; i + + ) {
Item * it = _get_item_at_pos ( it_from , it_to , i ) ;
2022-04-05 12:40:26 +02:00
Color color ;
2020-01-26 23:11:09 +01:00
if ( fbg_flag = = 0 ) {
color = _find_bgcolor ( it ) ;
} else {
color = _find_fgcolor ( it ) ;
}
bool change_to_color = ( ( color . a > 0 ) & & ( ( last_color . a - 0.0 ) < 0.01 ) ) ;
bool change_from_color = ( ( ( color . a - 0.0 ) < 0.01 ) & & ( last_color . a > 0.0 ) ) ;
bool change_color = ( ( ( color . a > 0 ) = = ( last_color . a > 0 ) ) & & ( color ! = last_color ) ) ;
if ( change_to_color ) {
fbg_index . x = MIN ( i , fbg_index . x ) ;
fbg_index . y = MAX ( i , fbg_index . y ) ;
}
if ( change_from_color | | change_color ) {
fbg_index . x = MIN ( i , fbg_index . x ) ;
fbg_index . y = MAX ( i , fbg_index . y ) ;
draw_box = true ;
}
if ( draw_box ) {
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( p_rid , fbg_index . x , fbg_index . y ) ;
for ( int j = 0 ; j < sel . size ( ) ; j + + ) {
Vector2 rect_off = line_off + Vector2 ( sel [ j ] . x , - TS - > shaped_text_get_ascent ( p_rid ) ) ;
Vector2 rect_size = Vector2 ( sel [ j ] . y - sel [ j ] . x , TS - > shaped_text_get_size ( p_rid ) . y ) ;
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( p_ci , Rect2 ( rect_off , rect_size ) , last_color ) ;
}
fbg_index = Vector2i ( end , start ) ;
draw_box = false ;
}
if ( change_color ) {
fbg_index . x = MIN ( i , fbg_index . x ) ;
fbg_index . y = MAX ( i , fbg_index . y ) ;
}
last_color = color ;
}
if ( last_color . a > 0 ) {
Vector < Vector2 > sel = TS - > shaped_text_get_selection ( p_rid , fbg_index . x , end ) ;
for ( int i = 0 ; i < sel . size ( ) ; i + + ) {
Vector2 rect_off = line_off + Vector2 ( sel [ i ] . x , - TS - > shaped_text_get_ascent ( p_rid ) ) ;
Vector2 rect_size = Vector2 ( sel [ i ] . y - sel [ i ] . x , TS - > shaped_text_get_size ( p_rid ) . y ) ;
RenderingServer : : get_singleton ( ) - > canvas_item_add_rect ( p_ci , Rect2 ( rect_off , rect_size ) , last_color ) ;
}
}
}
2018-11-10 03:44:58 +01:00
Ref < RichTextEffect > RichTextLabel : : _get_custom_effect_by_code ( String p_bbcode_identifier ) {
for ( int i = 0 ; i < custom_effects . size ( ) ; i + + ) {
2021-08-24 22:02:24 +02:00
Ref < RichTextEffect > effect = custom_effects [ i ] ;
if ( ! effect . is_valid ( ) ) {
2018-11-10 03:44:58 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-11-10 03:44:58 +01:00
2021-08-24 22:02:24 +02:00
if ( effect - > get_bbcode ( ) = = p_bbcode_identifier ) {
return effect ;
2018-11-10 03:44:58 +01:00
}
}
2019-09-25 22:05:42 +02:00
return Ref < RichTextEffect > ( ) ;
2018-11-10 03:44:58 +01:00
}
Dictionary RichTextLabel : : parse_expressions_for_values ( Vector < String > p_expressions ) {
Dictionary d = Dictionary ( ) ;
for ( int i = 0 ; i < p_expressions . size ( ) ; i + + ) {
String expression = p_expressions [ i ] ;
Array a = Array ( ) ;
Vector < String > parts = expression . split ( " = " , true ) ;
String key = parts [ 0 ] ;
if ( parts . size ( ) ! = 2 ) {
return d ;
}
Vector < String > values = parts [ 1 ] . split ( " , " , false ) ;
2020-02-06 21:51:36 +01:00
# ifdef MODULE_REGEX_ENABLED
2018-11-10 03:44:58 +01:00
RegEx color = RegEx ( ) ;
color . compile ( " ^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$ " ) ;
RegEx nodepath = RegEx ( ) ;
nodepath . compile ( " ^ \\ $ " ) ;
RegEx boolean = RegEx ( ) ;
boolean . compile ( " ^(true|false)$ " ) ;
RegEx decimal = RegEx ( ) ;
decimal . compile ( " ^-?^.? \\ d+( \\ . \\ d+?)?$ " ) ;
RegEx numerical = RegEx ( ) ;
numerical . compile ( " ^ \\ d+$ " ) ;
for ( int j = 0 ; j < values . size ( ) ; j + + ) {
if ( ! color . search ( values [ j ] ) . is_null ( ) ) {
a . append ( Color : : html ( values [ j ] ) ) ;
} else if ( ! nodepath . search ( values [ j ] ) . is_null ( ) ) {
if ( values [ j ] . begins_with ( " $ " ) ) {
String v = values [ j ] . substr ( 1 , values [ j ] . length ( ) ) ;
a . append ( NodePath ( v ) ) ;
}
} else if ( ! boolean . search ( values [ j ] ) . is_null ( ) ) {
if ( values [ j ] = = " true " ) {
a . append ( true ) ;
} else if ( values [ j ] = = " false " ) {
a . append ( false ) ;
}
} else if ( ! decimal . search ( values [ j ] ) . is_null ( ) ) {
2020-07-24 20:07:57 +02:00
a . append ( values [ j ] . to_float ( ) ) ;
2018-11-10 03:44:58 +01:00
} else if ( ! numerical . search ( values [ j ] ) . is_null ( ) ) {
a . append ( values [ j ] . to_int ( ) ) ;
} else {
a . append ( values [ j ] ) ;
}
}
2020-02-06 21:51:36 +01:00
# endif
2018-11-10 03:44:58 +01:00
if ( values . size ( ) > 1 ) {
d [ key ] = a ;
} else if ( values . size ( ) = = 1 ) {
d [ key ] = a [ 0 ] ;
}
}
return d ;
}
2022-03-04 09:18:44 +01:00
RichTextLabel : : RichTextLabel ( const String & p_text ) {
2017-03-05 16:44:50 +01:00
main = memnew ( ItemFrame ) ;
main - > index = 0 ;
current = main ;
2015-12-26 14:25:17 +01:00
main - > lines . resize ( 1 ) ;
2022-05-18 09:17:55 +02:00
main - > lines [ 0 ] . from = main ;
main - > first_invalid_line . store ( 0 ) ;
main - > first_resized_line . store ( 0 ) ;
main - > first_invalid_font_line . store ( 0 ) ;
2017-03-05 16:44:50 +01:00
current_frame = main ;
vscroll = memnew ( VScrollBar ) ;
2021-08-25 15:49:30 +02:00
add_child ( vscroll , false , INTERNAL_MODE_FRONT ) ;
2018-09-15 00:55:22 +02:00
vscroll - > set_drag_node ( String ( " .. " ) ) ;
2014-02-10 02:10:30 +01:00
vscroll - > set_step ( 1 ) ;
2020-12-22 17:24:29 +01:00
vscroll - > set_anchor_and_offset ( SIDE_TOP , ANCHOR_BEGIN , 0 ) ;
vscroll - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , 0 ) ;
vscroll - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , 0 ) ;
2020-02-21 18:28:45 +01:00
vscroll - > connect ( " value_changed " , callable_mp ( this , & RichTextLabel : : _scroll_changed ) ) ;
2014-02-10 02:10:30 +01:00
vscroll - > set_step ( 1 ) ;
vscroll - > hide ( ) ;
2019-11-01 15:17:40 +01:00
2022-03-04 09:18:44 +01:00
set_text ( p_text ) ;
2022-05-18 09:17:55 +02:00
updating . store ( false ) ;
stop_thread . store ( false ) ;
2022-03-04 09:18:44 +01:00
2017-01-09 19:50:08 +01:00
set_clip_contents ( true ) ;
2014-02-10 02:10:30 +01:00
}
RichTextLabel : : ~ RichTextLabel ( ) {
2022-05-18 09:17:55 +02:00
_stop_thread ( ) ;
2017-03-05 16:44:50 +01:00
memdelete ( main ) ;
2014-02-10 02:10:30 +01:00
}