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
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 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
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"
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 ( ) {
2021-07-17 23:22:52 +02:00
Ref < StyleBox > style = get_theme_stylebox ( SNAME ( " normal " ) ) ;
2017-09-02 18:20:04 +02:00
return Rect2 ( style - > get_offset ( ) , get_size ( ) - style - > get_minimum_size ( ) ) ;
}
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 : {
ItemText * t = ( ItemText * ) it ;
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 ;
case ITEM_NEWLINE :
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 ) {
String M [ ] = { " " , " M " , " MM " , " MMM " } ;
String C [ ] = { " " , " C " , " CC " , " CCC " , " CD " , " D " , " DC " , " DCC " , " DCCC " , " CM " } ;
String X [ ] = { " " , " X " , " XX " , " XXX " , " XL " , " L " , " LX " , " LXX " , " LXXX " , " XC " } ;
String I [ ] = { " " , " I " , " II " , " III " , " IV " , " V " , " VI " , " VII " , " VIII " , " IX " } ;
s = M [ p_num / 1000 ] + C [ ( p_num % 1000 ) / 100 ] + X [ ( p_num % 100 ) / 10 ] + I [ p_num % 10 ] ;
} else {
String M [ ] = { " " , " m " , " mm " , " mmm " } ;
String C [ ] = { " " , " c " , " cc " , " ccc " , " cd " , " d " , " dc " , " dcc " , " dccc " , " cm " } ;
String X [ ] = { " " , " x " , " xx " , " xxx " , " xl " , " l " , " lx " , " lxx " , " lxxx " , " xc " } ;
String I [ ] = { " " , " i " , " ii " , " iii " , " iv " , " v " , " vi " , " vii " , " viii " , " ix " } ;
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
2020-10-02 14:02:02 +02:00
void RichTextLabel : : _resize_line ( ItemFrame * p_frame , int p_line , const Ref < Font > & p_base_font , int p_base_font_size , int p_width ) {
ERR_FAIL_COND ( p_frame = = nullptr ) ;
ERR_FAIL_COND ( p_line < 0 | | p_line > = p_frame - > lines . size ( ) ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Line & l = p_frame - > lines . write [ p_line ] ;
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 ;
2021-02-12 14:06:39 +01:00
tabs . push_back ( tab_size * p_base_font - > get_char_size ( ' ' , 0 , 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
2020-10-02 14:02:02 +02:00
Item * it_to = ( p_line + 1 < p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
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 ) ;
2021-07-17 23:22:52 +02:00
int hseparation = get_theme_constant ( SNAME ( " table_hseparation " ) ) ;
int vseparation = get_theme_constant ( SNAME ( " table_vseparation " ) ) ;
2020-10-02 14:02:02 +02:00
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 + + ) {
table - > columns . write [ i ] . width = 0 ;
}
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 ) ;
2020-10-02 14:02:02 +02:00
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
_resize_line ( frame , i , p_base_font , p_base_font_size , 1 ) ;
}
idx + + ;
}
2016-10-17 21:03:47 +02:00
2020-10-02 14:02:02 +02:00
// Compute minimum width for each cell.
const int available_width = p_width - hseparation * ( 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 ;
table - > total_width = hseparation ;
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 ) {
table - > columns . write [ i ] . expand = true ;
}
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 + + ) {
table - > columns . write [ i ] . width = table - > columns [ i ] . min_width ;
if ( table - > columns [ i ] . expand & & total_ratio > 0 ) {
table - > columns . write [ i ] . width + = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
}
table - > total_width + = table - > columns [ i ] . width + hseparation ;
}
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 ;
table - > columns . write [ i ] . width = table - > columns [ i ] . max_width ;
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 ) ;
table - > columns . write [ i ] . width + = incr ;
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 ;
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
frame - > lines . write [ i ] . text_buf - > set_width ( table - > columns [ column ] . width ) ;
table - > columns . write [ column ] . width = MAX ( table - > columns . write [ column ] . width , ceil ( frame - > lines [ i ] . text_buf - > get_size ( ) . x ) ) ;
if ( i > 0 ) {
frame - > lines . write [ i ] . offset . y = frame - > lines [ i - 1 ] . offset . y + frame - > lines [ i - 1 ] . text_buf - > get_size ( ) . y ;
} else {
frame - > lines . write [ i ] . offset . y = 0 ;
}
2021-09-23 16:58:43 +02:00
frame - > lines . write [ i ] . offset + = offset ;
2020-10-02 14:02:02 +02:00
float h = frame - > lines [ i ] . text_buf - > get_size ( ) . y ;
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 ;
}
yofs + = frame - > padding . size . y ;
offset . x + = table - > columns [ column ] . width + hseparation + frame - > padding . size . x ;
row_height = MAX ( yofs , row_height ) ;
if ( column = = col_count - 1 ) {
offset . x = 0 ;
row_height + = vseparation ;
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
2020-10-02 14:02:02 +02:00
if ( p_line > 0 ) {
2021-09-02 22:06:54 +02:00
l . offset . y = p_frame - > lines [ p_line - 1 ] . offset . y + p_frame - > lines [ p_line - 1 ] . text_buf - > get_size ( ) . y + get_theme_constant ( SNAME ( " line_separation " ) ) ;
2020-10-02 14:02:02 +02:00
} else {
l . offset . y = 0 ;
}
}
void RichTextLabel : : _shape_line ( ItemFrame * p_frame , int p_line , const Ref < Font > & p_base_font , int p_base_font_size , int p_width , int * r_char_offset ) {
ERR_FAIL_COND ( p_frame = = nullptr ) ;
ERR_FAIL_COND ( p_line < 0 | | p_line > = p_frame - > lines . size ( ) ) ;
Line & l = p_frame - > lines . write [ p_line ] ;
// Clear cache.
l . text_buf - > clear ( ) ;
l . text_buf - > set_flags ( TextServer : : BREAK_MANDATORY | TextServer : : BREAK_WORD_BOUND | TextServer : : JUSTIFICATION_KASHIDA | TextServer : : JUSTIFICATION_WORD_BOUND | TextServer : : JUSTIFICATION_TRIM_EDGE_SPACES ) ;
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 ;
2021-02-12 14:06:39 +01:00
tabs . push_back ( tab_size * p_base_font - > get_char_size ( ' ' , 0 , p_base_font_size ) . width ) ;
2020-10-02 14:02:02 +02:00
l . text_buf - > tab_align ( tabs ) ;
}
// Shape current paragraph.
String text ;
Item * it_to = ( p_line + 1 < 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 ) ) {
2021-12-05 13:28:32 +01:00
if ( visible_chars_behavior = = 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.
const ItemDropcap * dc = ( ItemDropcap * ) it ;
if ( dc ! = nullptr ) {
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 ;
} else {
l . text_buf - > clear_dropcap ( ) ;
}
} break ;
2020-10-02 14:02:02 +02:00
case ITEM_NEWLINE : {
Ref < Font > font = _find_font ( it ) ;
if ( font . is_null ( ) ) {
font = p_base_font ;
}
int font_size = _find_font_size ( it ) ;
if ( font_size = = - 1 ) {
font_size = p_base_font_size ;
}
l . text_buf - > add_string ( " \n " , font , font_size , Dictionary ( ) , " " ) ;
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 : {
ItemText * t = ( ItemText * ) it ;
2017-03-05 16:44:50 +01:00
Ref < Font > font = _find_font ( it ) ;
2020-05-14 16:41:43 +02:00
if ( font . is_null ( ) ) {
2017-03-05 16:44:50 +01:00
font = p_base_font ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
int font_size = _find_font_size ( it ) ;
if ( font_size = = - 1 ) {
font_size = p_base_font_size ;
}
Dictionary font_ftr = _find_font_features ( it ) ;
String lang = _find_language ( it ) ;
String tx = t - > text ;
2021-12-05 13:28:32 +01:00
if ( visible_chars_behavior = = 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
2020-10-02 14:02:02 +02:00
l . text_buf - > add_string ( tx , font , font_size , font_ftr , lang ) ;
text + = tx ;
l . char_count + = tx . length ( ) ;
} break ;
case ITEM_IMAGE : {
ItemImage * img = ( ItemImage * ) it ;
l . text_buf - > add_object ( ( uint64_t ) it , img - > image - > get_size ( ) , img - > inline_align , 1 ) ;
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 ) ;
2021-07-17 23:22:52 +02:00
int hseparation = get_theme_constant ( SNAME ( " table_hseparation " ) ) ;
int vseparation = get_theme_constant ( SNAME ( " table_vseparation " ) ) ;
2020-10-02 14:02:02 +02:00
int col_count = table - > columns . size ( ) ;
int t_char_count = 0 ;
// Set minimums to zero.
for ( int i = 0 ; i < col_count ; i + + ) {
table - > columns . write [ i ] . min_width = 0 ;
table - > columns . write [ i ] . max_width = 0 ;
table - > columns . write [ i ] . width = 0 ;
}
// Compute minimum width for each cell.
const int available_width = p_width - hseparation * ( 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 ;
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
int char_offset = l . char_offset + l . char_count ;
_shape_line ( frame , i , p_base_font , p_base_font_size , 1 , & char_offset ) ;
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
table - > columns . write [ column ] . min_width = MAX ( table - > columns [ column ] . min_width , ceil ( frame - > lines [ i ] . text_buf - > get_size ( ) . x ) ) ;
2021-10-19 21:38:58 +02:00
table - > columns . write [ 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 ;
table - > total_width = hseparation ;
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 ) {
table - > columns . write [ i ] . expand = true ;
}
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 + + ) {
table - > columns . write [ i ] . width = table - > columns [ i ] . min_width ;
if ( table - > columns [ i ] . expand & & total_ratio > 0 ) {
table - > columns . write [ i ] . width + = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
2014-02-10 02:10:30 +01:00
}
2020-10-02 14:02:02 +02:00
table - > total_width + = table - > columns [ i ] . width + hseparation ;
}
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 ;
table - > columns . write [ i ] . width = table - > columns [ i ] . max_width ;
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 ) ;
table - > columns . write [ i ] . width + = incr ;
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 ;
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
frame - > lines . write [ i ] . text_buf - > set_width ( table - > columns [ column ] . width ) ;
table - > columns . write [ column ] . width = MAX ( table - > columns . write [ column ] . width , ceil ( frame - > lines [ i ] . text_buf - > get_size ( ) . x ) ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
if ( i > 0 ) {
frame - > lines . write [ i ] . offset . y = frame - > lines [ i - 1 ] . offset . y + frame - > lines [ i - 1 ] . text_buf - > get_size ( ) . y ;
} else {
frame - > lines . write [ i ] . offset . y = 0 ;
}
2021-09-23 16:58:43 +02:00
frame - > lines . write [ i ] . offset + = offset ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
float h = frame - > lines [ i ] . text_buf - > get_size ( ) . y ;
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 ;
}
yofs + = frame - > padding . size . y ;
offset . x + = table - > columns [ column ] . width + hseparation + 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 ;
row_height + = vseparation ;
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
2020-10-02 14:02:02 +02:00
if ( p_line > 0 ) {
2021-09-02 22:06:54 +02:00
l . offset . y = p_frame - > lines [ p_line - 1 ] . offset . y + p_frame - > lines [ p_line - 1 ] . text_buf - > get_size ( ) . y + get_theme_constant ( SNAME ( " line_separation " ) ) ;
2020-10-02 14:02:02 +02:00
} else {
l . offset . y = 0 ;
}
}
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 ) {
2020-10-02 14:02:02 +02:00
Vector2 off ;
2019-09-25 22:05:42 +02:00
2021-01-12 13:03:10 +01:00
ERR_FAIL_COND_V ( p_frame = = nullptr , 0 ) ;
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = p_frame - > lines . size ( ) , 0 ) ;
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
Line & l = p_frame - > lines . write [ p_line ] ;
2018-11-10 03:44:58 +01:00
2020-10-02 14:02:02 +02:00
Item * it_from = l . from ;
Item * it_to = ( p_line + 1 < p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
Variant meta ;
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
2021-12-05 13:28:32 +01:00
bool trim_chars = ( visible_characters > = 0 ) & & ( visible_chars_behavior = = VC_CHARS_AFTER_SHAPING ) ;
bool trim_glyphs_ltr = ( visible_characters > = 0 ) & & ( ( visible_chars_behavior = = VC_GLYPHS_LTR ) | | ( ( visible_chars_behavior = = VC_GLYPHS_AUTO ) & & ! lrtl ) ) ;
bool trim_glyphs_rtl = ( visible_characters > = 0 ) & & ( ( visible_chars_behavior = = VC_GLYPHS_RTL ) | | ( ( visible_chars_behavior = = VC_GLYPHS_AUTO ) & & lrtl ) ) ;
int total_glyphs = ( trim_glyphs_ltr | | trim_glyphs_rtl ) ? get_total_glyph_count ( ) : 0 ;
int visible_glyphs = total_glyphs * percent_visible ;
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 ( ) ) {
2020-10-02 14:02:02 +02:00
Ref < Font > font = _find_font ( l . from ) ;
if ( font . is_null ( ) ) {
2021-07-17 23:22:52 +02:00
font = get_theme_font ( SNAME ( " normal_font " ) ) ;
2020-10-02 14:02:02 +02:00
}
int font_size = _find_font_size ( l . from ) ;
if ( font_size = = - 1 ) {
2021-07-17 23:22:52 +02:00
font_size = get_theme_font_size ( SNAME ( " normal_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 + + ) {
RID rid = l . text_buf - > get_line_rid ( line ) ;
2021-01-12 13:03:10 +01:00
if ( p_ofs . y + off . y > = ctrl_size . height ) {
break ;
}
if ( p_ofs . y + off . y + TS - > shaped_text_get_size ( rid ) . y < = 0 ) {
off . y + = TS - > shaped_text_get_size ( rid ) . y ;
continue ;
}
2018-04-11 15:08:56 +02:00
2020-10-02 14:02:02 +02:00
float width = l . text_buf - > get_width ( ) ;
float length = TS - > shaped_text_get_width ( rid ) ;
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 ;
}
}
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
2021-03-29 16:26:53 +02:00
off . y + = TS - > shaped_text_get_ascent ( rid ) + l . text_buf - > get_spacing_top ( ) ;
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 + + ) {
Item * it = ( Item * ) ( uint64_t ) objects [ i ] ;
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 ) ;
2021-07-17 23:22:52 +02:00
Color odd_row_bg = get_theme_color ( SNAME ( " table_odd_row_bg " ) ) ;
Color even_row_bg = get_theme_color ( SNAME ( " table_even_row_bg " ) ) ;
Color border = get_theme_color ( SNAME ( " table_border " ) ) ;
int hseparation = get_theme_constant ( SNAME ( " table_hseparation " ) ) ;
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
2020-10-02 14:02:02 +02:00
for ( int j = 0 ; j < 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 ) ;
Color font_color = _find_outline_color ( it , p_outline_color ) ;
2021-11-05 23:28:48 +01:00
Color font_shadow_color = p_font_shadow_color ;
if ( ( size < = 0 | | font_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.
float faded_visibility = 1.0f ;
if ( fade ) {
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
}
2020-10-02 14:02:02 +02:00
font_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
2021-11-05 23:28:48 +01:00
bool visible = ( font_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 ;
double current_offset = Math : : range_lerp ( char_current_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
double previous_offset = Math : : range_lerp ( char_previous_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
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.
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 ( ) ) {
2021-11-05 23:28:48 +01:00
if ( font_shadow_color . a > 0 ) {
TS - > font_draw_glyph ( frid , ci , glyphs [ i ] . font_size , p_ofs + fx_offset + gloff + p_shadow_ofs , gl , font_shadow_color ) ;
}
if ( font_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 , font_shadow_color ) ;
}
if ( font_color . a ! = 0.0 & & size > 0 ) {
TS - > font_draw_glyph_outline ( frid , ci , glyphs [ i ] . font_size , size , p_ofs + fx_offset + gloff , gl , font_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.
2021-07-17 23:22:52 +02:00
Color selection_fg = get_theme_color ( SNAME ( " font_selected_color " ) ) ;
Color selection_bg = get_theme_color ( SNAME ( " 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 ) ;
}
}
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 ) ;
if ( _find_underline ( it ) | | ( _find_meta ( it , & meta ) & & underline_meta ) ) {
Color uc = font_color ;
uc . a * = 0.5 ;
float y_off = TS - > shaped_text_get_underline_position ( rid ) ;
2021-10-02 22:07:42 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * get_theme_default_base_scale ( ) ;
2020-10-02 14:02:02 +02:00
draw_line ( p_ofs + Vector2 ( off . x , off . y + y_off ) , p_ofs + Vector2 ( off . x + glyphs [ i ] . advance * glyphs [ i ] . repeat , off . y + y_off ) , uc , underline_width ) ;
} else if ( _find_strikethrough ( it ) ) {
Color uc = font_color ;
uc . a * = 0.5 ;
float y_off = - TS - > shaped_text_get_ascent ( rid ) + TS - > shaped_text_get_size ( rid ) . y / 2 ;
2021-10-02 22:07:42 +02:00
float underline_width = TS - > shaped_text_get_underline_thickness ( rid ) * get_theme_default_base_scale ( ) ;
2020-10-02 14:02:02 +02:00
draw_line ( p_ofs + Vector2 ( off . x , off . y + y_off ) , p_ofs + Vector2 ( off . x + glyphs [ i ] . advance * glyphs [ i ] . repeat , off . y + y_off ) , uc , underline_width ) ;
}
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.
float faded_visibility = 1.0f ;
if ( fade ) {
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 ;
double current_offset = Math : : range_lerp ( char_current_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
double previous_offset = Math : : range_lerp ( char_previous_rand % max_rand , 0 , max_rand , 0.0f , 2.f * ( float ) Math_PI ) ;
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 + + ) {
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 & & ( r_processed_glyphs > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( r_processed_glyphs < total_glyphs - visible_glyphs ) ) ;
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
}
off . x + = glyphs [ i ] . advance ;
}
}
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 ) ;
2021-03-29 16:26:53 +02:00
off . y + = TS - > shaped_text_get_descent ( rid ) + l . text_buf - > get_spacing_bottom ( ) ;
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
2020-10-02 14:02:02 +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 ) {
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.
int from_line = 0 ;
//TODO, change to binary search ?
while ( from_line < main - > lines . size ( ) ) {
2020-12-18 07:55:19 +01:00
if ( main - > lines [ from_line ] . offset . y + main - > lines [ from_line ] . text_buf - > get_size ( ) . y > = vofs ) {
2020-10-02 14:02:02 +02:00
break ;
}
from_line + + ;
}
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
if ( from_line > = main - > lines . size ( ) ) {
return ;
}
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 ) ;
while ( ofs . y < size . height & & from_line < main - > lines . size ( ) ) {
2021-03-14 18:54:48 +01: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 ) ;
2021-08-20 23:57:51 +02:00
ofs . y + = main - > lines [ from_line ] . text_buf - > get_size ( ) . y + get_theme_constant ( SNAME ( " 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
2020-10-02 14:02:02 +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 ) {
Vector2 off ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
int char_pos = - 1 ;
Line & l = p_frame - > lines . write [ p_line ] ;
bool rtl = ( l . text_buf - > get_direction ( ) = = TextServer : : DIRECTION_RTL ) ;
bool lrtl = is_layout_rtl ( ) ;
bool table_hit = false ;
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
2021-03-29 16:26:53 +02:00
off . y + = TS - > shaped_text_get_ascent ( rid ) + l . text_buf - > get_spacing_top ( ) ;
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 + + ) {
Item * it = ( Item * ) ( uint64_t ) objects [ i ] ;
if ( it ! = nullptr ) {
Rect2 rect = TS - > shaped_text_get_object_rect ( rid , objects [ i ] ) ;
if ( rect . has_point ( p_click - p_ofs - off ) ) {
switch ( it - > type ) {
case ITEM_TABLE : {
2021-07-17 23:22:52 +02:00
int hseparation = get_theme_constant ( SNAME ( " table_hseparation " ) ) ;
int vseparation = get_theme_constant ( SNAME ( " table_vseparation " ) ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
ItemTable * table = static_cast < ItemTable * > ( it ) ;
2015-12-26 14:25:17 +01:00
2020-10-02 14:02:02 +02:00
table_hit = true ;
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 ;
}
Rect2 crect = Rect2 ( p_ofs + off + rect . position + coff - frame - > padding . position , Size2 ( table - > columns [ col ] . width + hseparation , table - > rows [ row ] + vseparation ) + frame - > padding . position + frame - > padding . size ) ;
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 ) ) {
for ( int j = 0 ; j < frame - > lines . size ( ) ; j + + ) {
_find_click_in_line ( frame , j , p_ofs + off + rect . position + Vector2 ( 0 , frame - > lines [ j ] . offset . y ) , rect . size . x , p_click , r_click_frame , r_click_line , r_click_item , r_click_char ) ;
if ( ( ( r_click_item ! = nullptr ) & & ( ( * r_click_item ) ! = nullptr ) ) | | ( ( r_click_frame ! = nullptr ) & & ( ( * r_click_frame ) ! = nullptr ) ) ) {
return off . y ;
}
}
}
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
}
2020-10-02 14:02:02 +02:00
Rect2 rect = Rect2 ( p_ofs + off - Vector2 ( 0 , TS - > shaped_text_get_ascent ( rid ) ) , Size2 ( get_size ( ) . x , TS - > shaped_text_get_size ( rid ) . y ) ) ;
if ( rect . has_point ( p_click ) & & ! table_hit ) {
char_pos = TS - > shaped_text_hit_test_position ( rid , p_click . x - rect . position . x ) ;
}
2021-03-29 16:26:53 +02:00
off . y + = TS - > shaped_text_get_descent ( rid ) + l . text_buf - > get_spacing_bottom ( ) ;
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 ( char_pos > = 0 ) {
// Find item.
if ( r_click_item ! = nullptr ) {
Item * it = p_frame - > lines [ p_line ] . from ;
Item * it_to = ( p_line + 1 < p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
2021-03-14 18:54:48 +01:00
if ( char_pos = = p_frame - > lines [ p_line ] . char_count ) {
// Selection after the end of line, select last item.
if ( it_to ! = nullptr ) {
* r_click_item = _get_prev_item ( it_to ) ;
} else {
for ( Item * i = it ; i & & i ! = it_to ; i = _get_next_item ( i ) ) {
* r_click_item = i ;
}
}
} else {
// Selection in the line.
* r_click_item = _get_item_at_pos ( it , it_to , char_pos ) ;
}
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 ;
2014-02-10 02:10:30 +01:00
update ( ) ;
}
void RichTextLabel : : _update_scroll ( ) {
2018-04-11 15:53:13 +02:00
int total_height = get_content_height ( ) ;
2014-02-10 02:10:30 +01:00
bool exceeds = total_height > get_size ( ) . height & & scroll_active ;
2017-03-05 16:44:50 +01:00
if ( exceeds ! = scroll_visible ) {
2014-02-10 02:10:30 +01:00
if ( exceeds ) {
2017-03-05 16:44:50 +01:00
scroll_visible = true ;
scroll_w = vscroll - > get_combined_minimum_size ( ) . width ;
2014-02-10 02:10:30 +01:00
vscroll - > show ( ) ;
2020-12-22 17:24:29 +01:00
vscroll - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_END , - scroll_w ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
scroll_visible = false ;
scroll_w = 0 ;
2018-12-02 00:06:38 +01:00
vscroll - > hide ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-12-02 00:06:38 +01:00
2020-10-02 14:02:02 +02:00
main - > first_resized_line = 0 ; //invalidate ALL
2018-12-02 00:06:38 +01:00
_validate_line_caches ( main ) ;
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 ) ;
}
}
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 ;
update ( ) ;
}
} break ;
2014-02-10 02:10:30 +01:00
case NOTIFICATION_RESIZED : {
2020-10-02 14:02:02 +02:00
main - > first_resized_line = 0 ; //invalidate ALL
2014-02-10 02:10:30 +01:00
update ( ) ;
2015-04-21 21:01:58 +02:00
} break ;
2020-10-02 14:02:02 +02:00
case NOTIFICATION_THEME_CHANGED :
2015-04-21 21:01:58 +02:00
case NOTIFICATION_ENTER_TREE : {
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
2017-03-05 16:44:50 +01:00
main - > first_invalid_line = 0 ; //invalidate ALL
2015-04-21 21:01:58 +02:00
update ( ) ;
} break ;
2020-10-02 14:02:02 +02:00
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
case NOTIFICATION_TRANSLATION_CHANGED : {
main - > first_invalid_line = 0 ; //invalidate ALL
2019-08-02 04:50:14 +02:00
update ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
case NOTIFICATION_DRAW : {
2015-12-26 14:25:17 +01:00
_validate_line_caches ( main ) ;
2014-02-10 02:10:30 +01:00
_update_scroll ( ) ;
2017-03-05 16:44:50 +01:00
RID ci = get_canvas_item ( ) ;
2017-09-02 18:20:04 +02:00
2014-02-10 02:10:30 +01:00
Size2 size = get_size ( ) ;
2017-09-02 18:20:04 +02:00
Rect2 text_rect = _get_text_rect ( ) ;
2021-07-17 23:22:52 +02:00
draw_style_box ( get_theme_stylebox ( SNAME ( " normal " ) ) , 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 ) ;
2021-07-17 23:22:52 +02:00
draw_style_box ( get_theme_stylebox ( SNAME ( " focus " ) ) , 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
}
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.
2014-02-10 02:10:30 +01:00
int from_line = 0 ;
2020-10-02 14:02:02 +02:00
//TODO, change to binary search ?
2017-03-05 16:44:50 +01:00
while ( from_line < main - > lines . size ( ) ) {
2020-12-18 07:55:19 +01:00
if ( main - > lines [ from_line ] . offset . y + main - > lines [ from_line ] . text_buf - > get_size ( ) . y > = vofs ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-09-02 18:20:04 +02:00
from_line + + ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( from_line > = main - > lines . size ( ) ) {
2014-02-10 02:10:30 +01:00
break ; //nothing to draw
2020-05-14 16:41:43 +02:00
}
2021-07-17 23:22:52 +02:00
Ref < Font > base_font = get_theme_font ( SNAME ( " normal_font " ) ) ;
Color base_color = get_theme_color ( SNAME ( " default_color " ) ) ;
Color outline_color = get_theme_color ( SNAME ( " font_outline_color " ) ) ;
int outline_size = get_theme_constant ( SNAME ( " outline_size " ) ) ;
Color font_shadow_color = get_theme_color ( SNAME ( " font_shadow_color " ) ) ;
2021-11-05 23:28:48 +01:00
int shadow_outline_size = get_theme_constant ( SNAME ( " shadow_outline_size " ) ) ;
2021-07-17 23:22:52 +02:00
Point2 shadow_ofs ( get_theme_constant ( SNAME ( " shadow_offset_x " ) ) , get_theme_constant ( SNAME ( " 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 ;
2020-10-02 14:02:02 +02:00
while ( ofs . y < size . height & & from_line < main - > lines . size ( ) ) {
2021-01-12 13:03:10 +01:00
visible_paragraph_count + + ;
2021-12-05 13:28:32 +01:00
visible_line_count + = _draw_line ( main , from_line , ofs , text_rect . size . x , base_color , outline_size , outline_color , font_shadow_color , shadow_outline_size , shadow_ofs , processed_glyphs ) ;
2021-08-19 01:57:51 +02:00
ofs . y + = main - > lines [ from_line ] . text_buf - > get_size ( ) . y + get_theme_constant ( SNAME ( " line_separation " ) ) ;
2014-02-10 02:10:30 +01:00
from_line + + ;
}
2018-11-10 03:44:58 +01:00
} break ;
case NOTIFICATION_INTERNAL_PROCESS : {
2021-05-14 04:48:35 +02:00
if ( is_visible_in_tree ( ) ) {
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 ) ;
update ( ) ;
}
2021-10-21 23:02:46 +02:00
} break ;
case NOTIFICATION_FOCUS_EXIT : {
if ( deselect_on_focus_loss_enabled ) {
selection . active = false ;
update ( ) ;
}
} 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-05-14 16:41:43 +02:00
if ( main - > first_invalid_line < main - > lines . size ( ) ) {
2021-08-24 16:07:42 +02:00
return get_default_cursor_shape ( ) ; //invalid
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 ( main - > first_resized_line < main - > lines . size ( ) ) {
2021-08-24 16:07:42 +02:00
return get_default_cursor_shape ( ) ; //invalid
2020-10-02 14:02: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 ;
( ( RichTextLabel * ) ( this ) ) - > _find_click ( main , p_pos , nullptr , nullptr , & item , nullptr , & outside ) ;
2019-04-23 16:10:15 +02:00
2020-05-14 16:41:43 +02:00
if ( item & & ! outside & & ( ( 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
}
2019-04-23 16:10:15 +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 ( ) ) {
2020-05-14 16:41:43 +02:00
if ( main - > first_invalid_line < main - > lines . size ( ) ) {
2017-05-20 17:38:03 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
if ( main - > first_resized_line < main - > lines . size ( ) ) {
return ;
}
2014-02-10 02:10:30 +01:00
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
2020-10-02 14:02:02 +02:00
_find_click ( main , b - > get_position ( ) , & c_frame , & c_line , & c_item , & c_index , & outside ) ;
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 ) {
2020-10-02 14:02:02 +02:00
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 ;
2019-06-26 15:08:25 +02:00
selection . active = false ;
2017-12-04 16:46:20 +01:00
2019-06-26 15:08:25 +02:00
update ( ) ;
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
2020-10-02 14:02:02 +02:00
_find_click ( main , b - > get_position ( ) , & c_frame , & c_line , & c_item , & c_index , & outside ) ;
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 ] ;
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 ( ) ) ;
}
2020-10-02 14:02:02 +02:00
update ( ) ;
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 ;
2018-10-09 02:04:59 +02:00
2021-04-13 10:25:44 +02:00
if ( ! b - > is_double_click ( ) & & ! scroll_updated ) {
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 ;
_find_click ( main , b - > get_position ( ) , nullptr , nullptr , & c_item , nullptr , & outside ) ;
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
}
}
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 ( ) ) {
2021-07-17 23:22:52 +02:00
vscroll - > set_value ( vscroll - > get_value ( ) - get_theme_font ( SNAME ( " normal_font " ) ) - > get_height ( get_theme_font_size ( SNAME ( " normal_font_size " ) ) ) ) ;
2020-12-07 12:32:00 +01:00
handled = true ;
}
if ( k - > is_action ( " ui_down " ) & & vscroll - > is_visible_in_tree ( ) ) {
2021-07-17 23:22:52 +02:00
vscroll - > set_value ( vscroll - > get_value ( ) + get_theme_font ( SNAME ( " normal_font " ) ) - > get_height ( get_theme_font_size ( SNAME ( " 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 ;
}
if ( k - > is_action ( " ui_copy " ) ) {
selection_copy ( ) ;
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 ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( m . is_valid ( ) ) {
2020-05-14 16:41:43 +02:00
if ( main - > first_invalid_line < main - > lines . size ( ) ) {
2017-05-20 17:38:03 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
if ( main - > first_resized_line < main - > lines . size ( ) ) {
return ;
}
2014-02-10 02:10:30 +01:00
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
2020-10-02 14:02:02 +02:00
_find_click ( main , m - > get_position ( ) , & c_frame , & c_line , & c_item , & c_index , & outside ) ;
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 ;
2020-10-02 14:02:02 +02:00
if ( selection . from_item - > index > selection . to_item - > index ) {
2017-05-20 17:38:03 +02:00
swap = true ;
2020-10-02 14:02:02 +02:00
} else if ( selection . from_item - > index = = selection . to_item - > index ) {
2020-05-14 16:41:43 +02:00
if ( selection . from_char > selection . to_char ) {
2017-03-05 16:44:50 +01:00
swap = true ;
2020-05-14 16:41:43 +02:00
} else if ( selection . from_char = = selection . to_char ) {
2017-05-20 17:38:03 +02:00
selection . active = false ;
2021-10-12 09:43:50 +02:00
update ( ) ;
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 ;
update ( ) ;
}
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
}
}
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 ) {
* r_frame = ( ItemFrame * ) item - > parent ;
}
if ( r_line ! = nullptr ) {
* r_line = item - > line ;
}
return ;
}
item = item - > parent ;
}
}
2014-02-10 02:10:30 +01:00
Ref < Font > 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 ) ;
2014-02-10 02:10:30 +01:00
return fi - > font ;
}
2017-03-05 16:44:50 +01:00
fontitem = fontitem - > parent ;
2014-02-10 02:10:30 +01:00
}
return Ref < Font > ( ) ;
}
2020-10-02 14:02:02 +02:00
int RichTextLabel : : _find_font_size ( Item * p_item ) {
Item * sizeitem = p_item ;
while ( sizeitem ) {
if ( sizeitem - > type = = ITEM_FONT_SIZE ) {
ItemFontSize * fi = static_cast < ItemFontSize * > ( sizeitem ) ;
return fi - > font_size ;
}
sizeitem = sizeitem - > parent ;
}
return - 1 ;
}
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
}
Dictionary RichTextLabel : : _find_font_features ( Item * p_item ) {
Item * ffitem = p_item ;
while ( ffitem ) {
if ( ffitem - > type = = ITEM_FONT_FEATURES ) {
ItemFontFeatures * fi = static_cast < ItemFontFeatures * > ( ffitem ) ;
return fi - > opentype_features ;
}
ffitem = ffitem - > parent ;
}
return Dictionary ( ) ;
}
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 ) {
Ref < Font > font = _find_font ( item ) ;
2020-05-14 16:41:43 +02:00
if ( font . is_null ( ) ) {
2017-03-05 16:44:50 +01:00
font = p_base_font ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
int font_size = _find_font_size ( item ) ;
if ( font_size = = - 1 ) {
font_size = p_base_font_size ;
}
2021-02-12 14:06:39 +01:00
margin + = tab_size * font - > get_char_size ( ' ' , 0 , 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 ) {
Ref < Font > font = _find_font ( item ) ;
2020-05-14 16:41:43 +02:00
if ( font . is_null ( ) ) {
2017-03-05 16:44:50 +01:00
font = p_base_font ;
2020-05-14 16:41:43 +02:00
}
2020-10-02 14:02:02 +02:00
int font_size = _find_font_size ( item ) ;
if ( font_size = = - 1 ) {
font_size = p_base_font_size ;
}
2021-02-12 14:06:39 +01:00
margin + = tab_size * font - > get_char_size ( ' ' , 0 , 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 ;
}
}
Control : : StructuredTextParser RichTextLabel : : _find_stt ( Item * p_item ) {
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 ;
}
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 ;
}
void RichTextLabel : : _validate_line_caches ( ItemFrame * p_frame ) {
if ( p_frame - > first_invalid_line = = p_frame - > lines . size ( ) ) {
if ( p_frame - > first_resized_line = = p_frame - > lines . size ( ) ) {
return ;
}
// Resize lines without reshaping.
Size2 size = get_size ( ) ;
if ( fixed_width ! = - 1 ) {
size . width = fixed_width ;
}
Rect2 text_rect = _get_text_rect ( ) ;
2021-07-17 23:22:52 +02:00
Ref < Font > base_font = get_theme_font ( SNAME ( " normal_font " ) ) ;
int base_font_size = get_theme_font_size ( SNAME ( " normal_font_size " ) ) ;
2020-10-02 14:02:02 +02:00
for ( int i = p_frame - > first_resized_line ; i < p_frame - > lines . size ( ) ; i + + ) {
_resize_line ( p_frame , i , base_font , base_font_size , text_rect . get_size ( ) . width - scroll_w ) ;
}
int total_height = 0 ;
if ( p_frame - > lines . size ( ) ) {
2020-12-18 07:55:19 +01:00
total_height = p_frame - > lines [ p_frame - > lines . size ( ) - 1 ] . offset . y + p_frame - > lines [ p_frame - > lines . size ( ) - 1 ] . text_buf - > get_size ( ) . y ;
2020-10-02 14:02:02 +02:00
}
p_frame - > first_resized_line = p_frame - > lines . size ( ) ;
2019-06-20 12:42:25 +02:00
2020-10-02 14:02:02 +02:00
updating_scroll = true ;
vscroll - > set_max ( total_height ) ;
2020-12-18 07:55:19 +01:00
vscroll - > set_page ( text_rect . size . height ) ;
2020-10-02 14:02:02 +02:00
if ( scroll_follow & & scroll_following ) {
vscroll - > set_value ( total_height - size . height ) ;
2019-06-20 12:42:25 +02:00
}
2020-10-02 14:02:02 +02:00
updating_scroll = false ;
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
}
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-10-02 14:02:02 +02:00
// Shape invalid lines.
2014-02-10 02:10:30 +01:00
Size2 size = get_size ( ) ;
2018-07-20 23:14:33 +02:00
if ( fixed_width ! = - 1 ) {
size . width = fixed_width ;
}
2017-09-02 18:20:04 +02:00
Rect2 text_rect = _get_text_rect ( ) ;
2014-02-10 02:10:30 +01:00
2021-07-17 23:22:52 +02:00
Ref < Font > base_font = get_theme_font ( SNAME ( " normal_font " ) ) ;
int base_font_size = get_theme_font_size ( SNAME ( " normal_font_size " ) ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
int total_chars = ( p_frame - > first_invalid_line = = 0 ) ? 0 : ( p_frame - > lines [ p_frame - > first_invalid_line ] . char_offset + p_frame - > lines [ p_frame - > first_invalid_line ] . char_count ) ;
2017-03-05 16:44:50 +01:00
for ( int i = p_frame - > first_invalid_line ; i < p_frame - > lines . size ( ) ; i + + ) {
2020-10-02 14:02:02 +02:00
_shape_line ( p_frame , i , base_font , base_font_size , text_rect . get_size ( ) . width - scroll_w , & total_chars ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
int total_height = 0 ;
2020-05-14 16:41:43 +02:00
if ( p_frame - > lines . size ( ) ) {
2020-12-18 07:55:19 +01:00
total_height = p_frame - > lines [ p_frame - > lines . size ( ) - 1 ] . offset . y + p_frame - > lines [ p_frame - > lines . size ( ) - 1 ] . text_buf - > get_size ( ) . y ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
p_frame - > first_invalid_line = p_frame - > lines . size ( ) ;
p_frame - > first_resized_line = p_frame - > lines . size ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
updating_scroll = true ;
2014-02-10 02:10:30 +01:00
vscroll - > set_max ( total_height ) ;
2020-12-18 07:55:19 +01:00
vscroll - > set_page ( text_rect . size . height ) ;
2020-05-14 16:41:43 +02:00
if ( scroll_follow & & scroll_following ) {
2017-03-05 16:44:50 +01:00
vscroll - > set_value ( total_height - size . height ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
updating_scroll = false ;
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
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : _invalidate_current_line ( ItemFrame * p_frame ) {
if ( p_frame - > lines . size ( ) - 1 < = p_frame - > first_invalid_line ) {
p_frame - > first_invalid_line = p_frame - > lines . size ( ) - 1 ;
2014-02-10 02:10:30 +01:00
update ( ) ;
}
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : add_text ( const String & p_text ) {
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 ) {
2018-07-25 03:11:03 +02:00
current_frame - > lines . write [ 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
}
}
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 ) {
ItemText * t = ( ItemText * ) p_item ;
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 ) {
2018-07-25 03:11:03 +02:00
current_frame - > lines . write [ 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
}
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-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 ) {
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 ( ) {
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 ) ;
2014-02-10 02:10:30 +01:00
}
2017-01-31 22:57:46 +01:00
bool RichTextLabel : : remove_line ( const int p_line ) {
2020-05-14 16:41:43 +02:00
if ( p_line > = 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 ) {
2018-07-25 03:11:03 +02:00
main - > lines . write [ 0 ] . from = main ;
2020-05-14 16:41:43 +02:00
}
2017-01-31 22:57:46 +01:00
2020-10-02 14:02:02 +02:00
main - > first_invalid_line = 0 ; // p_line ???
2021-05-25 13:48:28 +02:00
update ( ) ;
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 ) {
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 ) ;
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : push_font ( const Ref < Font > & p_font ) {
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 ;
_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 ( ) {
2021-07-17 23:22:52 +02:00
Ref < Font > normal_font = get_theme_font ( SNAME ( " normal_font " ) ) ;
2019-10-16 15:22:56 +02:00
ERR_FAIL_COND ( normal_font . is_null ( ) ) ;
push_font ( normal_font ) ;
}
void RichTextLabel : : push_bold ( ) {
2021-07-17 23:22:52 +02:00
Ref < Font > bold_font = get_theme_font ( SNAME ( " bold_font " ) ) ;
2019-10-16 15:22:56 +02:00
ERR_FAIL_COND ( bold_font . is_null ( ) ) ;
push_font ( bold_font ) ;
}
void RichTextLabel : : push_bold_italics ( ) {
2021-07-17 23:22:52 +02:00
Ref < Font > bold_italics_font = get_theme_font ( SNAME ( " bold_italics_font " ) ) ;
2019-10-16 15:22:56 +02:00
ERR_FAIL_COND ( bold_italics_font . is_null ( ) ) ;
push_font ( bold_italics_font ) ;
}
void RichTextLabel : : push_italics ( ) {
2021-07-17 23:22:52 +02:00
Ref < Font > italics_font = get_theme_font ( SNAME ( " italics_font " ) ) ;
2019-10-16 15:22:56 +02:00
ERR_FAIL_COND ( italics_font . is_null ( ) ) ;
push_font ( italics_font ) ;
}
void RichTextLabel : : push_mono ( ) {
2021-07-17 23:22:52 +02:00
Ref < Font > mono_font = get_theme_font ( SNAME ( " mono_font " ) ) ;
2019-10-16 15:22:56 +02:00
ERR_FAIL_COND ( mono_font . is_null ( ) ) ;
push_font ( mono_font ) ;
}
2020-10-02 14:02:02 +02:00
void RichTextLabel : : push_font_size ( int p_font_size ) {
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemFontSize * item = memnew ( ItemFontSize ) ;
item - > font_size = p_font_size ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_font_features ( const Dictionary & p_features ) {
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemFontFeatures * item = memnew ( ItemFontFeatures ) ;
item - > opentype_features = p_features ;
_add_item ( item , true ) ;
}
void RichTextLabel : : push_outline_size ( int p_font_size ) {
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemOutlineSize * item = memnew ( ItemOutlineSize ) ;
item - > outline_size = p_font_size ;
_add_item ( item , true ) ;
}
2017-03-05 16:44:50 +01:00
void RichTextLabel : : push_color ( const Color & p_color ) {
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 ) {
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 ( ) {
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 ( ) {
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemStrikethrough * item = memnew ( ItemStrikethrough ) ;
_add_item ( item , true ) ;
}
2021-11-25 03:58:47 +01:00
void RichTextLabel : : push_paragraph ( HorizontalAlignment p_alignment , Control : : TextDirection p_direction , const String & p_language , Control : : StructuredTextParser p_st_parser ) {
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 ) {
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 ) {
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 ) {
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
}
2021-11-25 03:58:47 +01:00
void RichTextLabel : : push_table ( int p_columns , InlineAlignment p_alignment ) {
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 ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < item - > columns . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
item - > columns . write [ i ] . expand = false ;
item - > columns . write [ 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 ) {
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 ) {
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 ) {
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 ) {
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 ) {
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 ) {
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 ) {
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 ) {
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 ) {
ERR_FAIL_COND ( current - > type ! = ITEM_TABLE ) ;
ItemTable * table = static_cast < ItemTable * > ( current ) ;
ERR_FAIL_INDEX ( p_column , table - > columns . size ( ) ) ;
2018-07-25 03:11:03 +02:00
table - > columns . write [ p_column ] . expand = p_expand ;
table - > columns . write [ 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 ) {
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
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 ) {
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
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 ) {
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
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 ) {
ERR_FAIL_COND ( current - > type ! = ITEM_FRAME ) ;
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 ( ) {
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 ) ;
2020-04-02 01:20:12 +02:00
item - > lines . write [ 0 ] . from = nullptr ;
2020-10-02 14:02:02 +02:00
item - > first_invalid_line = 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 ( ) {
ERR_FAIL_COND ( ! current - > parent ) ;
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 ( ) {
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 ) ;
2017-03-05 16:44:50 +01:00
main - > first_invalid_line = 0 ;
2014-02-10 02:10:30 +01:00
update ( ) ;
2020-10-02 14:02:02 +02:00
selection . click_frame = nullptr ;
selection . click_item = nullptr ;
2017-03-05 16:44:50 +01:00
selection . active = false ;
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 ) {
2017-03-05 16:44:50 +01:00
tab_size = p_spaces ;
2020-10-02 14:02:02 +02:00
main - > first_resized_line = 0 ;
2014-02-10 02:10:30 +01:00
update ( ) ;
}
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 ) {
2017-03-05 16:44:50 +01:00
underline_meta = p_underline ;
2014-02-10 02:10:30 +01:00
update ( ) ;
}
bool RichTextLabel : : is_meta_underlined ( ) const {
return underline_meta ;
}
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 ) ;
2014-02-10 02:10:30 +01:00
update ( ) ;
}
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 ) {
2014-02-10 02:10:30 +01:00
int pos = 0 ;
List < String > tag_stack ;
2021-07-17 23:22:52 +02:00
Ref < Font > normal_font = get_theme_font ( SNAME ( " normal_font " ) ) ;
Ref < Font > bold_font = get_theme_font ( SNAME ( " bold_font " ) ) ;
Ref < Font > italics_font = get_theme_font ( SNAME ( " italics_font " ) ) ;
Ref < Font > bold_italics_font = get_theme_font ( SNAME ( " bold_italics_font " ) ) ;
Ref < Font > mono_font = get_theme_font ( SNAME ( " mono_font " ) ) ;
2015-04-21 21:01:58 +02:00
2021-07-17 23:22:52 +02:00
Color base_color = get_theme_color ( SNAME ( " default_color " ) ) ;
2014-02-10 02:10:30 +01:00
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 ;
2015-04-21 21:01:58 +02:00
2018-11-10 03:44:58 +01:00
set_process_internal ( false ) ;
2017-03-05 16:44:50 +01:00
while ( pos < p_bbcode . length ( ) ) {
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
if ( brk_pos > pos ) {
2017-03-05 16:44:50 +01:00
add_text ( p_bbcode . substr ( pos , brk_pos - pos ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( brk_pos = = p_bbcode . length ( ) ) {
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
2017-03-05 16:44:50 +01:00
add_text ( p_bbcode . substr ( brk_pos , p_bbcode . length ( ) - brk_pos ) ) ;
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 ;
typedef Map < String , String > OptionMap ;
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 ) {
2018-11-10 03:44:58 +01:00
add_text ( " [ " + tag ) ;
pos = brk_end ;
2014-02-10 02:10:30 +01:00
continue ;
}
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
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else 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 ) {
2015-04-21 21:01:58 +02:00
push_font ( bold_italics_font ) ;
2020-05-14 16:41:43 +02:00
} else {
2015-04-21 21:01:58 +02:00
push_font ( bold_font ) ;
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 ) {
2015-04-21 21:01:58 +02:00
push_font ( bold_italics_font ) ;
2020-05-14 16:41:43 +02:00
} else {
2015-04-21 21:01:58 +02:00
push_font ( italics_font ) ;
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
2015-04-21 21:01:58 +02:00
push_font ( mono_font ) ;
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 ) ;
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 ) ;
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 ;
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 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 ;
Control : : StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT ;
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 " ) {
st_parser = STRUCTURED_TEXT_DEFAULT ;
} else if ( subtag_a [ 1 ] = = " u " | | subtag_a [ 1 ] = = " uri " ) {
st_parser = STRUCTURED_TEXT_URI ;
} else if ( subtag_a [ 1 ] = = " f " | | subtag_a [ 1 ] = = " file " ) {
st_parser = STRUCTURED_TEXT_FILE ;
} else if ( subtag_a [ 1 ] = = " e " | | subtag_a [ 1 ] = = " email " ) {
st_parser = STRUCTURED_TEXT_EMAIL ;
} else if ( subtag_a [ 1 ] = = " l " | | subtag_a [ 1 ] = = " list " ) {
st_parser = STRUCTURED_TEXT_LIST ;
} else if ( subtag_a [ 1 ] = = " n " | | subtag_a [ 1 ] = = " none " ) {
st_parser = STRUCTURED_TEXT_NONE ;
} else if ( subtag_a [ 1 ] = = " c " | | subtag_a [ 1 ] = = " custom " ) {
st_parser = STRUCTURED_TEXT_CUSTOM ;
}
}
}
}
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 " ) ;
2020-11-19 15:45:23 +01:00
} else if ( tag . begins_with ( " dropcap " ) ) {
Vector < String > subtag = tag . substr ( 5 , tag . length ( ) ) . split ( " " ) ;
2021-07-17 23:22:52 +02:00
Ref < Font > f = get_theme_font ( SNAME ( " normal_font " ) ) ;
int fs = get_theme_font_size ( SNAME ( " normal_font_size " ) ) * 3 ;
Color color = get_theme_color ( SNAME ( " default_color " ) ) ;
Color outline_color = get_theme_color ( SNAME ( " outline_color " ) ) ;
int outline_size = get_theme_constant ( SNAME ( " 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 ) ;
OptionMap : : Element * color_option = bbcode_options . find ( " color " ) ;
if ( color_option ) {
2020-11-16 14:31:58 +01: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 {
OptionMap : : Element * width_option = bbcode_options . find ( " width " ) ;
if ( width_option ) {
width = width_option - > value ( ) . to_int ( ) ;
}
2019-10-16 12:00:15 +02:00
2020-05-28 08:17:17 +02:00
OptionMap : : Element * height_option = bbcode_options . find ( " height " ) ;
if ( height_option ) {
height = height_option - > value ( ) . to_int ( ) ;
}
}
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 ( ) ) ;
2020-11-16 14:31:58 +01:00
Color color = Color : : from_string ( color_str , base_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 ( ) ) ;
2021-01-08 09:18:12 +01:00
Color color = Color : : from_string ( color_str , base_color ) ;
2020-10-02 14:02:02 +02:00
push_outline_color ( color ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " outline_color " ) ;
2014-02-10 02:10:30 +01:00
} else if ( tag . begins_with ( " font= " ) ) {
2017-03-05 16:44:50 +01:00
String fnt = tag . substr ( 5 , tag . length ( ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Ref < Font > font = ResourceLoader : : load ( fnt , " Font " ) ;
2020-05-14 16:41:43 +02:00
if ( font . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
push_font ( font ) ;
2020-05-14 16:41:43 +02:00
} else {
2015-04-21 21:01:58 +02:00
push_font ( normal_font ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01: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 ( " font " ) ;
2020-10-02 14:02:02 +02:00
} 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 " ) ;
} else if ( tag . begins_with ( " opentype_features= " ) ) {
String fnt_ftr = tag . substr ( 18 , tag . length ( ) ) ;
Vector < String > subtag = fnt_ftr . split ( " , " ) ;
Dictionary ftrs ;
for ( int i = 0 ; i < subtag . size ( ) ; i + + ) {
Vector < String > subtag_a = subtag [ i ] . split ( " = " ) ;
if ( subtag_a . size ( ) = = 2 ) {
ftrs [ TS - > name_to_tag ( subtag_a [ 0 ] ) ] = subtag_a [ 1 ] . to_int ( ) ;
} else if ( subtag_a . size ( ) = = 1 ) {
ftrs [ TS - > name_to_tag ( subtag_a [ 0 ] ) ] = 1 ;
}
}
push_font_features ( ftrs ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " opentype_features " ) ;
} else if ( tag . begins_with ( " font " ) ) {
Vector < String > subtag = tag . substr ( 2 , tag . length ( ) ) . split ( " " ) ;
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 ] ;
Ref < Font > font = ResourceLoader : : load ( fnt , " Font " ) ;
if ( font . is_valid ( ) ) {
push_font ( font ) ;
} else {
push_font ( normal_font ) ;
}
} else if ( subtag_a [ 0 ] = = " size " | | subtag_a [ 0 ] = = " s " ) {
int fnt_size = subtag_a [ 1 ] . to_int ( ) ;
push_font_size ( fnt_size ) ;
}
}
}
pos = brk_end + 1 ;
tag_stack . push_front ( " font " ) ;
} else if ( tag . begins_with ( " outline_size= " ) ) {
int fnt_size = tag . substr ( 13 , tag . length ( ) ) . to_int ( ) ;
push_outline_size ( fnt_size ) ;
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 ;
OptionMap : : Element * start_option = bbcode_options . find ( " start " ) ;
if ( start_option ) {
start_index = start_option - > value ( ) . to_int ( ) ;
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
int length = 10 ;
OptionMap : : Element * length_option = bbcode_options . find ( " length " ) ;
if ( length_option ) {
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 ;
2020-05-28 08:17:17 +02:00
OptionMap : : Element * strength_option = bbcode_options . find ( " level " ) ;
if ( strength_option ) {
strength = strength_option - > value ( ) . to_int ( ) ;
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float rate = 20.0f ;
OptionMap : : Element * rate_option = bbcode_options . find ( " rate " ) ;
if ( rate_option ) {
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 ;
2020-05-28 08:17:17 +02:00
OptionMap : : Element * amplitude_option = bbcode_options . find ( " amp " ) ;
if ( amplitude_option ) {
amplitude = amplitude_option - > value ( ) . to_float ( ) ;
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float period = 5.0f ;
OptionMap : : Element * period_option = bbcode_options . find ( " freq " ) ;
if ( period_option ) {
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 ;
2020-05-28 08:17:17 +02:00
OptionMap : : Element * radius_option = bbcode_options . find ( " radius " ) ;
if ( radius_option ) {
radius = radius_option - > value ( ) . to_float ( ) ;
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float frequency = 1.0f ;
OptionMap : : Element * frequency_option = bbcode_options . find ( " freq " ) ;
if ( frequency_option ) {
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 ;
2020-05-28 08:17:17 +02:00
OptionMap : : Element * saturation_option = bbcode_options . find ( " sat " ) ;
if ( saturation_option ) {
saturation = saturation_option - > value ( ) . to_float ( ) ;
}
2018-11-10 03:44:58 +01:00
float value = 0.8f ;
2020-05-28 08:17:17 +02:00
OptionMap : : Element * value_option = bbcode_options . find ( " val " ) ;
if ( value_option ) {
value = value_option - > value ( ) . to_float ( ) ;
}
2018-11-10 03:44:58 +01:00
2020-05-28 08:17:17 +02:00
float frequency = 1.0f ;
OptionMap : : Element * frequency_option = bbcode_options . find ( " freq " ) ;
if ( frequency_option ) {
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 ( ) ) ;
Color color = Color : : from_string ( color_str , base_color ) ;
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 ( ) ) ;
Color color = Color : : from_string ( color_str , base_color ) ;
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 ) {
ERR_FAIL_INDEX ( p_paragraph , main - > lines . size ( ) ) ;
_validate_line_caches ( main ) ;
vscroll - > set_value ( main - > lines [ p_paragraph ] . offset . y ) ;
}
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 ) {
2015-12-26 14:25:17 +01:00
_validate_line_caches ( main ) ;
2021-01-12 13:03:10 +01:00
int line_count = 0 ;
for ( int i = 0 ; i < main - > lines . size ( ) ; i + + ) {
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 + + ) {
line_offset + = main - > lines [ i ] . text_buf - > get_line_size ( j ) . y ;
}
vscroll - > set_value ( main - > lines [ i ] . offset . y + line_offset ) ;
return ;
}
line_count + = main - > lines [ i ] . text_buf - > get_line_count ( ) ;
}
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 ;
for ( int i = 0 ; i < main - > lines . size ( ) ; i + + ) {
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 ) {
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 ) {
2017-03-05 16:44:50 +01:00
selection . active = false ;
2014-02-10 02:10:30 +01:00
update ( ) ;
}
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 ) {
deselect_on_focus_loss_enabled = p_enabled ;
if ( p_enabled & & selection . active & & ! has_focus ( ) ) {
selection . active = false ;
update ( ) ;
}
}
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 ) {
for ( int i = frame - > lines . size ( ) - 1 ; i > = 0 ; i - - ) {
if ( _search_line ( frame , i , p_string , - 1 , p_reverse_search ) ) {
return true ;
}
}
} else {
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
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 ) ;
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = p_frame - > lines . size ( ) , false ) ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
Line & l = p_frame - > lines . write [ p_line ] ;
2014-02-10 02:10:30 +01:00
2020-10-02 14:02:02 +02:00
String text ;
Item * it_to = ( p_line + 1 < p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
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 : {
ItemText * t = ( ItemText * ) it ;
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 ;
int ending_line = main - > lines . size ( ) - 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 ) ;
update ( ) ;
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 ) ;
update ( ) ;
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 ) {
current_line = main - > lines . size ( ) - 1 ;
} else if ( current_line > = main - > lines . size ( ) ) {
current_line = 0 ;
}
if ( _search_line ( main , current_line , p_string , char_idx , p_search_previous ) ) {
scroll_to_line ( current_line ) ;
update ( ) ;
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 ;
2020-10-02 14:02:02 +02:00
ERR_FAIL_COND_V ( p_frame = = nullptr , text ) ;
ERR_FAIL_COND_V ( p_line < 0 | | p_line > = p_frame - > lines . size ( ) , text ) ;
2014-02-16 01:16:33 +01:00
2020-10-02 14:02:02 +02:00
Line & l = p_frame - > lines . write [ p_line ] ;
2014-02-16 01:16:33 +01:00
2020-10-02 14:02:02 +02:00
Item * it_to = ( p_line + 1 < p_frame - > lines . size ( ) ) ? p_frame - > lines [ p_line + 1 ] . from : nullptr ;
int end_idx = 0 ;
if ( it_to ! = nullptr ) {
end_idx = it_to - > index ;
} else {
for ( Item * it = l . from ; it & & it ! = it_to ; it = _get_next_item ( it ) ) {
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 ) ;
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
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
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 ;
for ( int i = 0 ; i < main - > lines . size ( ) ; i + + ) {
text + = _get_line_text ( main , i , selection ) ;
}
2020-09-14 20:14:27 +02:00
return text ;
}
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
}
}
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 ) {
text = p_bbcode ;
2020-05-14 16:41:43 +02:00
if ( is_inside_tree ( ) & & 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 ) {
const ItemDropcap * dc = ( ItemDropcap * ) it ;
if ( dc ! = nullptr ) {
text + = dc - > text ;
}
} 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 ) ;
if ( text_direction ! = p_text_direction ) {
text_direction = p_text_direction ;
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
update ( ) ;
}
}
2017-07-03 21:59:58 +02:00
2020-10-02 14:02:02 +02:00
void RichTextLabel : : set_structured_text_bidi_override ( Control : : StructuredTextParser p_parser ) {
if ( st_parser ! = p_parser ) {
st_parser = p_parser ;
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
update ( ) ;
2017-07-03 21:59:58 +02:00
}
2020-10-02 14:02:02 +02:00
}
Control : : StructuredTextParser RichTextLabel : : get_structured_text_bidi_override ( ) const {
return st_parser ;
}
void RichTextLabel : : set_structured_text_bidi_override_options ( Array p_args ) {
st_args = p_args ;
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
2017-07-03 21:59:58 +02:00
update ( ) ;
}
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 ) {
language = p_language ;
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
update ( ) ;
}
}
String RichTextLabel : : get_language ( ) const {
return language ;
}
void RichTextLabel : : set_percent_visible ( float p_percent ) {
if ( percent_visible ! = p_percent ) {
if ( p_percent < 0 | | p_percent > = 1 ) {
visible_characters = - 1 ;
percent_visible = 1 ;
} else {
visible_characters = get_total_character_count ( ) * p_percent ;
percent_visible = p_percent ;
}
2021-12-05 13:28:32 +01:00
if ( visible_chars_behavior = = VC_CHARS_BEFORE_SHAPING ) {
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
}
2020-10-02 14:02:02 +02:00
update ( ) ;
}
}
2017-07-03 21:59:58 +02:00
float RichTextLabel : : get_percent_visible ( ) const {
return percent_visible ;
}
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 ;
if ( rteffect . is_valid ( ) ) {
custom_effects . push_back ( effect ) ;
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
}
}
2019-11-01 15:17:40 +01:00
int RichTextLabel : : get_content_height ( ) const {
2018-04-11 15:53:13 +02:00
int total_height = 0 ;
2020-05-14 16:41:43 +02:00
if ( main - > lines . size ( ) ) {
2020-12-18 07:55:19 +01:00
total_height = main - > lines [ main - > lines . size ( ) - 1 ] . offset . y + main - > lines [ main - > lines . size ( ) - 1 ] . text_buf - > get_size ( ) . y ;
2020-05-14 16:41:43 +02:00
}
2018-04-11 15:53:13 +02:00
return total_height ;
}
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)
// Altough some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty
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 ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " push_font " , " font " ) , & 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 ) ;
ClassDB : : bind_method ( D_METHOD ( " push_font_features " , " opentype_features " ) , & RichTextLabel : : push_font_features ) ;
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 ) ;
2021-11-25 03:58:47 +01:00
ClassDB : : bind_method ( D_METHOD ( " push_paragraph " , " alignment " , " base_direction " , " language " , " st_parser " ) , & RichTextLabel : : push_paragraph , DEFVAL ( TextServer : : DIRECTION_AUTO ) , DEFVAL ( " " ) , DEFVAL ( 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 ) ;
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 ) ;
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
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
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll " ) , & RichTextLabel : : get_v_scroll ) ;
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
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 ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selected_text " ) , & RichTextLabel : : get_selected_text ) ;
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
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 ) ;
2017-07-03 21:59:58 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_percent_visible " , " percent_visible " ) , & RichTextLabel : : set_percent_visible ) ;
ClassDB : : bind_method ( D_METHOD ( " get_percent_visible " ) , & RichTextLabel : : get_percent_visible ) ;
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 ) ;
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 ) ;
2017-03-05 16:44:50 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " visible_characters " , PROPERTY_HINT_RANGE , " -1,128000,1 " ) , " set_visible_characters " , " get_visible_characters " ) ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 19:20:53 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " percent_visible " , PROPERTY_HINT_RANGE , " 0,1,0.001 " ) , " set_percent_visible " , " get_percent_visible " ) ;
2018-01-11 23:35:12 +01:00
2021-12-05 13:28:32 +01:00
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 " ) ;
2018-01-11 23:35:12 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " meta_underlined " ) , " set_meta_underline " , " is_meta_underlined " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " tab_size " , PROPERTY_HINT_RANGE , " 0,24,1 " ) , " set_tab_size " , " get_tab_size " ) ;
2018-09-27 09:09:41 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2020-05-29 16:25:12 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " bbcode_enabled " ) , " set_use_bbcode " , " is_using_bbcode " ) ;
2018-01-11 23:35:12 +01: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 " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " selection_enabled " ) , " set_selection_enabled " , " is_selection_enabled " ) ;
2017-09-27 19:24:05 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " override_selected_font_color " ) , " set_override_selected_font_color " , " is_overriding_selected_font_color " ) ;
2014-02-10 02:10:30 +01:00
2021-10-21 23:02:46 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " deselect_on_focus_loss_enabled " ) , " set_deselect_on_focus_loss_enabled " , " is_deselect_on_focus_loss_enabled " ) ;
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 " ) ;
2018-11-10 03:44:58 +01:00
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 " ) ;
2020-10-02 14:02:02 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " language " ) , " set_language " , " get_language " ) ;
ADD_GROUP ( " Structured Text " , " structured_text_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " structured_text_bidi_override " , PROPERTY_HINT_ENUM , " Default,URI,File,Email,List,None,Custom " ) , " set_structured_text_bidi_override " , " get_structured_text_bidi_override " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " structured_text_bidi_override_options " ) , " set_structured_text_bidi_override_options " , " get_structured_text_bidi_override_options " ) ;
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
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 ) ;
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
BIND_ENUM_CONSTANT ( VC_CHARS_BEFORE_SHAPING ) ;
BIND_ENUM_CONSTANT ( VC_CHARS_AFTER_SHAPING ) ;
BIND_ENUM_CONSTANT ( VC_GLYPHS_AUTO ) ;
BIND_ENUM_CONSTANT ( VC_GLYPHS_LTR ) ;
BIND_ENUM_CONSTANT ( VC_GLYPHS_RTL ) ;
}
RichTextLabel : : VisibleCharactersBehavior RichTextLabel : : get_visible_characters_behavior ( ) const {
return visible_chars_behavior ;
}
void RichTextLabel : : set_visible_characters_behavior ( RichTextLabel : : VisibleCharactersBehavior p_behavior ) {
if ( visible_chars_behavior ! = p_behavior ) {
visible_chars_behavior = p_behavior ;
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
update ( ) ;
}
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 ) {
visible_characters = p_visible ;
if ( p_visible = = - 1 ) {
percent_visible = 1 ;
} else {
int total_char_count = get_total_character_count ( ) ;
if ( total_char_count > 0 ) {
percent_visible = ( float ) p_visible / ( float ) total_char_count ;
}
2021-01-02 11:46:44 +01:00
}
2021-12-05 13:28:32 +01:00
if ( visible_chars_behavior = = VC_CHARS_BEFORE_SHAPING ) {
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
}
2021-09-20 08:35:24 +02:00
update ( ) ;
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
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 ) ;
for ( int i = 0 ; i < f - > lines . size ( ) ; i + + ) {
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 ) {
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 {
2021-07-17 23:22:52 +02:00
Ref < StyleBox > style = get_theme_stylebox ( SNAME ( " normal " ) ) ;
2021-03-29 16:26:53 +02:00
Size2 size = style - > get_minimum_size ( ) ;
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
}
if ( fixed_width ! = - 1 | | fit_content_height ) {
2018-07-20 23:14:33 +02:00
const_cast < RichTextLabel * > ( this ) - > _validate_line_caches ( main ) ;
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
}
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 ) ;
Color color = Color ( 0 , 0 , 0 , 0 ) ;
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 ;
}
2014-02-10 02:10:30 +01:00
RichTextLabel : : RichTextLabel ( ) {
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 ) ;
2018-07-25 03:11:03 +02:00
main - > lines . write [ 0 ] . from = main ;
2017-03-05 16:44:50 +01:00
main - > first_invalid_line = 0 ;
2020-10-02 14:02:02 +02:00
main - > first_resized_line = 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
2017-01-09 19:50:08 +01:00
set_clip_contents ( true ) ;
2014-02-10 02:10:30 +01:00
}
RichTextLabel : : ~ RichTextLabel ( ) {
2017-03-05 16:44:50 +01:00
memdelete ( main ) ;
2014-02-10 02:10:30 +01:00
}