2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* rich_text_label.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2020-01-01 10:16:22 +00:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +00: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-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "rich_text_label.h"
2018-09-14 22:55:22 +00:00
2018-11-10 02:44:58 +00:00
# include "core/math/math_defs.h"
2018-09-11 16:13:45 +00:00
# include "core/os/keyboard.h"
# include "core/os/os.h"
2017-03-05 15:44:50 +00:00
# include "scene/scene_string_names.h"
2020-03-03 13:36:29 +00:00
# include "servers/display_server.h"
2020-02-06 20:51:36 +00:00
# include "modules/modules_enabled.gen.h"
# ifdef MODULE_REGEX_ENABLED
# include "modules/regex/regex.h"
# endif
2017-12-15 17:40:45 +00:00
# ifdef TOOLS_ENABLED
2018-09-14 22:55:22 +00:00
# include "editor/editor_scale.h"
2017-12-15 17:40:45 +00:00
# endif
2017-03-05 15:44:50 +00:00
RichTextLabel : : Item * RichTextLabel : : _get_next_item ( Item * p_item , bool p_free ) {
2014-02-10 01:10:30 +00:00
2015-12-26 13:25:17 +00:00
if ( p_free ) {
2014-02-10 01:10:30 +00:00
2015-12-26 13:25:17 +00:00
if ( p_item - > subitems . size ( ) ) {
return p_item - > subitems . front ( ) - > get ( ) ;
} else if ( ! p_item - > parent ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2015-12-26 13:25:17 +00: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 15:44:50 +00:00
p_item = p_item - > parent ;
2015-12-26 13:25:17 +00:00
}
if ( p_item - > parent )
return p_item - > E - > next ( ) - > get ( ) ;
else
2020-04-01 23:20:12 +00:00
return nullptr ;
2014-02-10 01:10:30 +00:00
}
2015-12-26 13:25:17 +00:00
} else {
2017-03-05 15:44:50 +00:00
if ( p_item - > subitems . size ( ) & & p_item - > type ! = ITEM_TABLE ) {
2014-02-10 01:10:30 +00:00
2015-12-26 13:25:17 +00:00
return p_item - > subitems . front ( ) - > get ( ) ;
2017-03-05 15:44:50 +00:00
} else if ( p_item - > type = = ITEM_FRAME ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2015-12-26 13:25:17 +00: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 15:44:50 +00:00
while ( p_item - > type ! = ITEM_FRAME & & ! p_item - > E - > next ( ) ) {
p_item = p_item - > parent ;
2015-12-26 13:25:17 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_item - > type ! = ITEM_FRAME )
2015-12-26 13:25:17 +00:00
return p_item - > E - > next ( ) - > get ( ) ;
else
2020-04-01 23:20:12 +00:00
return nullptr ;
2015-12-26 13:25:17 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-04-01 23:20:12 +00:00
return nullptr ;
2014-02-10 01:10:30 +00:00
}
2017-12-22 18:09:31 +00:00
RichTextLabel : : Item * RichTextLabel : : _get_prev_item ( Item * p_item , bool p_free ) {
if ( p_free ) {
if ( p_item - > subitems . size ( ) ) {
return p_item - > subitems . back ( ) - > get ( ) ;
} else if ( ! p_item - > parent ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2017-12-22 18:09:31 +00: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 ;
}
if ( p_item - > parent )
return p_item - > E - > prev ( ) - > get ( ) ;
else
2020-04-01 23:20:12 +00:00
return nullptr ;
2017-12-22 18:09:31 +00: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-01 23:20:12 +00:00
return nullptr ;
2017-12-22 18:09:31 +00: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 ;
}
if ( p_item - > type ! = ITEM_FRAME )
return p_item - > E - > prev ( ) - > get ( ) ;
else
2020-04-01 23:20:12 +00:00
return nullptr ;
2017-12-22 18:09:31 +00:00
}
}
2020-04-01 23:20:12 +00:00
return nullptr ;
2017-12-22 18:09:31 +00:00
}
2017-09-02 16:20:04 +00:00
Rect2 RichTextLabel : : _get_text_rect ( ) {
2020-03-12 12:37:40 +00:00
Ref < StyleBox > style = get_theme_stylebox ( " normal " ) ;
2017-09-02 16:20:04 +00:00
return Rect2 ( style - > get_offset ( ) , get_size ( ) - style - > get_minimum_size ( ) ) ;
}
2018-11-10 02:44:58 +00:00
2018-04-11 13:08:56 +00:00
int RichTextLabel : : _process_line ( ItemFrame * p_frame , const Vector2 & p_ofs , int & y , int p_width , int p_line , ProcessMode p_mode , const Ref < Font > & p_base_font , const Color & p_base_color , const Color & p_font_color_shadow , bool p_shadow_as_outline , const Point2 & shadow_ofs , const Point2i & p_click_pos , Item * * r_click_item , int * r_click_char , bool * r_outside , int p_char_count ) {
2015-12-28 21:04:22 +00:00
2019-10-05 17:17:07 +00:00
ERR_FAIL_INDEX_V ( ( int ) p_mode , 3 , 0 ) ;
2014-02-10 01:10:30 +00:00
RID ci ;
if ( r_outside )
2017-03-05 15:44:50 +00:00
* r_outside = false ;
if ( p_mode = = PROCESS_DRAW ) {
ci = get_canvas_item ( ) ;
2014-02-10 01:10:30 +00:00
if ( r_click_item )
2020-04-01 23:20:12 +00:00
* r_click_item = nullptr ;
2014-02-10 01:10:30 +00:00
}
2018-07-25 01:11:03 +00:00
Line & l = p_frame - > lines . write [ p_line ] ;
2014-02-10 01:10:30 +00:00
Item * it = l . from ;
2017-03-05 15:44:50 +00:00
int line_ofs = 0 ;
int margin = _find_margin ( it , p_base_font ) ;
Align align = _find_align ( it ) ;
int line = 0 ;
int spaces = 0 ;
2014-02-10 01:10:30 +00:00
2016-10-17 19:03:47 +00:00
int height = get_size ( ) . y ;
2017-03-05 15:44:50 +00:00
if ( p_mode ! = PROCESS_CACHE ) {
2014-02-10 01:10:30 +00:00
2016-10-17 19:03:47 +00:00
ERR_FAIL_INDEX_V ( line , l . offset_caches . size ( ) , 0 ) ;
2014-02-10 01:10:30 +00:00
line_ofs = l . offset_caches [ line ] ;
}
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_CACHE ) {
2015-04-21 19:01:58 +00:00
l . offset_caches . clear ( ) ;
l . height_caches . clear ( ) ;
2018-01-14 17:12:09 +00:00
l . ascent_caches . clear ( ) ;
l . descent_caches . clear ( ) ;
2017-03-05 15:44:50 +00:00
l . char_count = 0 ;
l . minimum_width = 0 ;
2018-03-18 10:28:17 +00:00
l . maximum_width = 0 ;
2015-04-21 19:01:58 +00:00
}
2017-03-05 15:44:50 +00:00
int wofs = margin ;
int spaces_size = 0 ;
int align_ofs = 0 ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode ! = PROCESS_CACHE & & align ! = ALIGN_FILL )
wofs + = line_ofs ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int begin = wofs ;
2014-02-10 01:10:30 +00:00
Ref < Font > cfont = _find_font ( it ) ;
if ( cfont . is_null ( ) )
2017-03-05 15:44:50 +00:00
cfont = p_base_font ;
2014-02-10 01:10:30 +00:00
2017-03-24 20:45:31 +00:00
//line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed
2017-03-05 15:44:50 +00:00
int line_height = cfont - > get_height ( ) ;
2018-01-14 17:12:09 +00:00
int line_ascent = cfont - > get_ascent ( ) ;
int line_descent = cfont - > get_descent ( ) ;
2014-02-10 01:10:30 +00:00
2020-01-31 05:05:52 +00:00
int backtrack = 0 ; // for dynamic hidden content.
2016-10-17 19:03:47 +00:00
int nonblank_line_count = 0 ; //number of nonblank lines as counted during PROCESS_DRAW
2014-02-10 01:10:30 +00:00
Variant meta ;
2016-10-17 19:03:47 +00:00
# define RETURN return nonblank_line_count
2017-03-05 15:44:50 +00:00
# define NEW_LINE \
{ \
if ( p_mode ! = PROCESS_CACHE ) { \
line + + ; \
2020-01-31 05:05:52 +00:00
backtrack = 0 ; \
2016-10-17 19:03:47 +00:00
if ( ! line_is_blank ) { \
nonblank_line_count + + ; \
} \
line_is_blank = true ; \
2017-03-05 15:44:50 +00:00
if ( line < l . offset_caches . size ( ) ) \
line_ofs = l . offset_caches [ line ] ; \
wofs = margin ; \
if ( align ! = ALIGN_FILL ) \
wofs + = line_ofs ; \
} else { \
int used = wofs - margin ; \
switch ( align ) { \
case ALIGN_LEFT : l . offset_caches . push_back ( 0 ) ; break ; \
case ALIGN_CENTER : l . offset_caches . push_back ( ( ( p_width - margin ) - used ) / 2 ) ; break ; \
case ALIGN_RIGHT : l . offset_caches . push_back ( ( ( p_width - margin ) - used ) ) ; break ; \
2018-10-25 23:19:15 +00:00
case ALIGN_FILL : l . offset_caches . push_back ( line_wrapped ? ( ( p_width - margin ) - used ) : 0 ) ; break ; \
2017-03-05 15:44:50 +00:00
} \
l . height_caches . push_back ( line_height ) ; \
2018-01-14 17:12:09 +00:00
l . ascent_caches . push_back ( line_ascent ) ; \
l . descent_caches . push_back ( line_descent ) ; \
2017-03-05 15:44:50 +00:00
l . space_caches . push_back ( spaces ) ; \
} \
2018-10-25 23:19:15 +00:00
line_wrapped = false ; \
2020-03-12 12:37:40 +00:00
y + = line_height + get_theme_constant ( SceneStringNames : : get_singleton ( ) - > line_separation ) ; \
2017-03-05 15:44:50 +00:00
line_height = 0 ; \
2018-01-14 17:12:09 +00:00
line_ascent = 0 ; \
line_descent = 0 ; \
2017-03-05 15:44:50 +00:00
spaces = 0 ; \
spaces_size = 0 ; \
wofs = begin ; \
align_ofs = 0 ; \
if ( p_mode ! = PROCESS_CACHE ) { \
lh = line < l . height_caches . size ( ) ? l . height_caches [ line ] : 1 ; \
2018-01-14 17:12:09 +00:00
line_ascent = line < l . ascent_caches . size ( ) ? l . ascent_caches [ line ] : 1 ; \
line_descent = line < l . descent_caches . size ( ) ? l . descent_caches [ line ] : 1 ; \
2017-03-05 15:44:50 +00:00
} \
if ( p_mode = = PROCESS_POINTER & & r_click_item & & p_click_pos . y > = p_ofs . y + y & & p_click_pos . y < = p_ofs . y + y + lh & & p_click_pos . x < p_ofs . x + wofs ) { \
if ( r_outside ) * r_outside = true ; \
* r_click_item = it ; \
* r_click_char = rchar ; \
2016-10-17 19:03:47 +00:00
RETURN ; \
2017-03-05 15:44:50 +00:00
} \
2014-02-10 01:10:30 +00:00
}
2020-01-08 00:44:27 +00:00
# define ENSURE_WIDTH(m_width) \
if ( p_mode = = PROCESS_CACHE ) { \
l . maximum_width = MAX ( l . maximum_width , MIN ( p_width , wofs + m_width ) ) ; \
l . minimum_width = MAX ( l . minimum_width , m_width ) ; \
} \
2020-01-31 05:05:52 +00:00
if ( wofs - backtrack + m_width > p_width ) { \
2020-01-08 00:44:27 +00:00
line_wrapped = true ; \
if ( p_mode = = PROCESS_CACHE ) { \
if ( spaces > 0 ) \
spaces - = 1 ; \
} \
const bool x_in_range = ( p_click_pos . x > p_ofs . x + wofs ) & & ( ! p_frame - > cell | | p_click_pos . x < p_ofs . x + p_width ) ; \
if ( p_mode = = PROCESS_POINTER & & r_click_item & & p_click_pos . y > = p_ofs . y + y & & p_click_pos . y < = p_ofs . y + y + lh & & x_in_range ) { \
if ( r_outside ) * r_outside = true ; \
* r_click_item = it ; \
* r_click_char = rchar ; \
RETURN ; \
} \
NEW_LINE \
2017-03-05 15:44:50 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
# define ADVANCE(m_width) \
{ \
if ( p_mode = = PROCESS_POINTER & & r_click_item & & p_click_pos . y > = p_ofs . y + y & & p_click_pos . y < = p_ofs . y + y + lh & & p_click_pos . x > = p_ofs . x + wofs & & p_click_pos . x < p_ofs . x + wofs + m_width ) { \
if ( r_outside ) * r_outside = false ; \
* r_click_item = it ; \
* r_click_char = rchar ; \
2016-10-17 19:03:47 +00:00
RETURN ; \
2017-03-05 15:44:50 +00:00
} \
wofs + = m_width ; \
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
# define CHECK_HEIGHT(m_height) \
if ( m_height > line_height ) { \
line_height = m_height ; \
}
2014-02-10 01:10:30 +00:00
2016-10-17 19:03:47 +00:00
# define YRANGE_VISIBLE(m_top, m_height) \
( m_height > 0 & & ( ( m_top > = 0 & & m_top < height ) | | ( ( m_top + m_height - 1 ) > = 0 & & ( m_top + m_height - 1 ) < height ) ) )
2014-02-10 01:10:30 +00:00
Color selection_fg ;
Color selection_bg ;
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_DRAW ) {
2020-03-12 12:37:40 +00:00
selection_fg = get_theme_color ( " font_color_selected " ) ;
selection_bg = get_theme_color ( " selection_color " ) ;
2014-02-10 01:10:30 +00:00
}
2016-10-17 19:03:47 +00:00
2017-03-05 15:44:50 +00:00
int rchar = 0 ;
int lh = 0 ;
2016-10-17 19:03:47 +00:00
bool line_is_blank = true ;
2018-10-25 23:19:15 +00:00
bool line_wrapped = false ;
2018-03-02 18:19:46 +00:00
int fh = 0 ;
2014-02-10 01:10:30 +00:00
while ( it ) {
2017-03-05 15:44:50 +00:00
switch ( it - > type ) {
2014-02-10 01:10:30 +00:00
2019-06-14 20:38:33 +00:00
case ITEM_ALIGN : {
ItemAlign * align_it = static_cast < ItemAlign * > ( it ) ;
align = align_it - > align ;
2019-06-20 10:42:25 +00:00
} break ;
case ITEM_INDENT : {
if ( it ! = l . from ) {
ItemIndent * indent_it = static_cast < ItemIndent * > ( it ) ;
int indent = indent_it - > level * tab_size * cfont - > get_char_size ( ' ' ) . width ;
margin + = indent ;
begin + = indent ;
wofs + = indent ;
}
2019-06-14 20:38:33 +00:00
} break ;
2014-02-10 01:10:30 +00:00
case ITEM_TEXT : {
2017-03-05 15:44:50 +00:00
ItemText * text = static_cast < ItemText * > ( it ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Ref < Font > font = _find_font ( it ) ;
2014-02-10 01:10:30 +00:00
if ( font . is_null ( ) )
2017-03-05 15:44:50 +00:00
font = p_base_font ;
2014-02-10 01:10:30 +00:00
2016-03-08 23:00:52 +00:00
const CharType * c = text - > text . c_str ( ) ;
2017-03-05 15:44:50 +00:00
const CharType * cf = c ;
2014-02-10 01:10:30 +00:00
int ascent = font - > get_ascent ( ) ;
2018-01-14 17:12:09 +00:00
int descent = font - > get_descent ( ) ;
2014-02-10 01:10:30 +00:00
Color color ;
2018-04-11 13:08:56 +00:00
Color font_color_shadow ;
2017-03-05 15:44:50 +00:00
bool underline = false ;
2018-09-22 21:41:13 +00:00
bool strikethrough = false ;
2020-04-01 23:20:12 +00:00
ItemFade * fade = nullptr ;
2018-11-10 02:44:58 +00:00
int it_char_start = p_char_count ;
Vector < ItemFX * > fx_stack = Vector < ItemFX * > ( ) ;
2019-09-25 20:05:42 +00:00
_fetch_item_fx_stack ( text , fx_stack ) ;
2018-11-10 02:44:58 +00:00
bool custom_fx_ok = true ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_DRAW ) {
color = _find_color ( text , p_base_color ) ;
2018-04-11 13:08:56 +00:00
font_color_shadow = _find_color ( text , p_font_color_shadow ) ;
2018-09-22 21:41:13 +00:00
if ( _find_underline ( text ) | | ( _find_meta ( text , & meta ) & & underline_meta ) ) {
2017-03-05 15:44:50 +00:00
underline = true ;
2018-09-22 21:41:13 +00:00
} else if ( _find_strikethrough ( text ) ) {
strikethrough = true ;
2014-02-10 01:10:30 +00:00
}
2019-09-25 20:05:42 +00:00
Item * fade_item = it ;
while ( fade_item ) {
if ( fade_item - > type = = ITEM_FADE ) {
fade = static_cast < ItemFade * > ( fade_item ) ;
break ;
}
fade_item = fade_item - > parent ;
}
2018-11-10 02:44:58 +00:00
2017-03-05 15:44:50 +00:00
} else if ( p_mode = = PROCESS_CACHE ) {
l . char_count + = text - > text . length ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
rchar = 0 ;
2018-03-17 08:44:34 +00:00
FontDrawer drawer ( font , Color ( 1 , 1 , 1 ) ) ;
2017-03-05 15:44:50 +00:00
while ( * c ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int end = 0 ;
int w = 0 ;
int fw = 0 ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
lh = 0 ;
2020-01-31 05:05:52 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode ! = PROCESS_CACHE ) {
lh = line < l . height_caches . size ( ) ? l . height_caches [ line ] : 1 ;
2018-01-14 17:12:09 +00:00
line_ascent = line < l . ascent_caches . size ( ) ? l . ascent_caches [ line ] : 1 ;
line_descent = line < l . descent_caches . size ( ) ? l . descent_caches [ line ] : 1 ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
while ( c [ end ] ! = 0 & & ! ( end & & c [ end - 1 ] = = ' ' & & c [ end ] ! = ' ' ) ) {
2015-04-21 19:01:58 +00:00
2017-03-05 15:44:50 +00:00
int cw = font - > get_char_size ( c [ end ] , c [ end + 1 ] ) . width ;
if ( c [ end ] = = ' \t ' ) {
cw = tab_size * font - > get_char_size ( ' ' ) . width ;
2015-05-09 21:09:58 +00:00
}
2016-06-14 11:09:27 +00:00
2020-04-13 09:29:38 +00:00
if ( end > 0 & & fw + cw + begin > p_width ) {
2016-06-14 11:09:27 +00:00
break ; //don't allow lines longer than assigned width
}
2017-03-05 15:44:50 +00:00
fw + = cw ;
2015-04-21 19:01:58 +00:00
2016-03-08 23:00:52 +00:00
end + + ;
2014-02-10 01:10:30 +00:00
}
2018-03-02 18:19:46 +00:00
CHECK_HEIGHT ( fh ) ;
2020-04-13 09:29:38 +00:00
ENSURE_WIDTH ( fw ) ;
2015-04-21 19:01:58 +00:00
2018-03-02 18:19:46 +00:00
line_ascent = MAX ( line_ascent , ascent ) ;
line_descent = MAX ( line_descent , descent ) ;
fh = line_ascent + line_descent ;
2018-03-01 17:03:53 +00:00
2017-03-05 15:44:50 +00:00
if ( end & & c [ end - 1 ] = = ' ' ) {
if ( p_mode = = PROCESS_CACHE ) {
spaces_size + = font - > get_char_size ( ' ' ) . width ;
} else if ( align = = ALIGN_FILL ) {
int ln = MIN ( l . offset_caches . size ( ) - 1 , line ) ;
2015-12-28 04:40:23 +00:00
if ( l . space_caches [ ln ] ) {
align_ofs = spaces * l . offset_caches [ ln ] / l . space_caches [ ln ] ;
}
2015-04-21 19:01:58 +00:00
}
2015-12-28 04:40:23 +00:00
spaces + + ;
2015-04-21 19:01:58 +00:00
}
2014-02-10 01:10:30 +00:00
{
2020-01-31 05:05:52 +00:00
int ofs = 0 - backtrack ;
2015-04-21 19:01:58 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < end ; i + + ) {
int pofs = wofs + ofs ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_POINTER & & r_click_char & & p_click_pos . y > = p_ofs . y + y & & p_click_pos . y < = p_ofs . y + y + lh ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int cw = font - > get_char_size ( c [ i ] , c [ i + 1 ] ) . x ;
2015-04-21 19:01:58 +00:00
2017-03-05 15:44:50 +00:00
if ( c [ i ] = = ' \t ' ) {
cw = tab_size * font - > get_char_size ( ' ' ) . width ;
2015-05-09 21:09:58 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_click_pos . x - cw / 2 > p_ofs . x + align_ofs + pofs ) {
2016-06-14 11:09:27 +00:00
2017-03-05 15:44:50 +00:00
rchar = int ( ( & c [ i ] ) - cf ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
ofs + = cw ;
} else if ( p_mode = = PROCESS_DRAW ) {
bool selected = false ;
2018-11-10 02:44:58 +00:00
Color fx_color = Color ( color ) ;
Point2 fx_offset ;
CharType fx_char = c [ i ] ;
2014-02-10 01:10:30 +00:00
if ( selection . active ) {
2017-03-05 15:44:50 +00:00
int cofs = ( & c [ i ] ) - cf ;
if ( ( text - > index > selection . from - > index | | ( text - > index = = selection . from - > index & & cofs > = selection . from_char ) ) & & ( text - > index < selection . to - > index | | ( text - > index = = selection . to - > index & & cofs < = selection . to_char ) ) ) {
selected = true ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
int cw = 0 ;
2018-11-10 02:44:58 +00:00
int c_item_offset = p_char_count - it_char_start ;
float faded_visibility = 1.0f ;
if ( fade ) {
if ( c_item_offset > = fade - > starting_index ) {
faded_visibility - = ( float ) ( c_item_offset - fade - > starting_index ) / ( float ) fade - > length ;
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility ;
}
fx_color . a = faded_visibility ;
}
bool visible = visible_characters < 0 | | ( ( p_char_count < visible_characters & & YRANGE_VISIBLE ( y + lh - line_descent - line_ascent , line_ascent + line_descent ) ) & &
faded_visibility > 0.0f ) ;
2020-01-31 05:05:52 +00:00
const bool previously_visible = visible ;
2018-11-10 02:44:58 +00:00
for ( int j = 0 ; j < fx_stack . size ( ) ; j + + ) {
2020-01-31 05:05:52 +00:00
2019-09-25 20:05:42 +00:00
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 ;
2018-11-10 02:44:58 +00:00
if ( ! custom_effect . is_null ( ) ) {
charfx - > elapsed_time = item_custom - > elapsed_time ;
charfx - > relative_index = c_item_offset ;
charfx - > absolute_index = p_char_count ;
charfx - > visibility = visible ;
charfx - > offset = fx_offset ;
charfx - > color = fx_color ;
charfx - > character = fx_char ;
bool effect_status = custom_effect - > _process_effect_impl ( charfx ) ;
custom_fx_ok = effect_status ;
fx_offset + = charfx - > offset ;
fx_color = charfx - > color ;
visible & = charfx - > visibility ;
fx_char = charfx - > character ;
}
2019-09-25 20:05:42 +00:00
} else if ( item_fx - > type = = ITEM_SHAKE ) {
ItemShake * item_shake = static_cast < ItemShake * > ( item_fx ) ;
2018-11-10 02:44:58 +00:00
uint64_t char_current_rand = item_shake - > offset_random ( c_item_offset ) ;
uint64_t char_previous_rand = item_shake - > offset_previous_random ( c_item_offset ) ;
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 ;
fx_offset + = 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 ;
2019-09-25 20:05:42 +00:00
} else if ( item_fx - > type = = ITEM_WAVE ) {
ItemWave * item_wave = static_cast < ItemWave * > ( item_fx ) ;
2018-11-10 02:44:58 +00:00
double value = Math : : sin ( item_wave - > frequency * item_wave - > elapsed_time + ( ( p_ofs . x + pofs ) / 50 ) ) * ( item_wave - > amplitude / 10.0f ) ;
fx_offset + = Point2 ( 0 , 1 ) * value ;
2019-09-25 20:05:42 +00:00
} else if ( item_fx - > type = = ITEM_TORNADO ) {
ItemTornado * item_tornado = static_cast < ItemTornado * > ( item_fx ) ;
2018-11-10 02:44:58 +00:00
double torn_x = Math : : sin ( item_tornado - > frequency * item_tornado - > elapsed_time + ( ( p_ofs . x + pofs ) / 50 ) ) * ( item_tornado - > radius ) ;
double torn_y = Math : : cos ( item_tornado - > frequency * item_tornado - > elapsed_time + ( ( p_ofs . x + pofs ) / 50 ) ) * ( item_tornado - > radius ) ;
fx_offset + = Point2 ( torn_x , torn_y ) ;
2019-09-25 20:05:42 +00:00
} else if ( item_fx - > type = = ITEM_RAINBOW ) {
ItemRainbow * item_rainbow = static_cast < ItemRainbow * > ( item_fx ) ;
2018-11-10 02:44:58 +00:00
fx_color = fx_color . from_hsv ( item_rainbow - > frequency * ( item_rainbow - > elapsed_time + ( ( p_ofs . x + pofs ) / 50 ) ) ,
item_rainbow - > saturation ,
item_rainbow - > value ,
fx_color . a ) ;
}
}
2015-06-01 22:42:34 +00:00
2020-04-07 12:43:46 +00:00
if ( visible ) {
2016-10-17 19:03:47 +00:00
line_is_blank = false ;
2020-04-07 12:43:46 +00:00
w + = font - > get_char_size ( c [ i ] , c [ i + 1 ] ) . x ;
}
2016-10-17 19:03:47 +00:00
2017-03-05 15:44:50 +00:00
if ( c [ i ] = = ' \t ' )
visible = false ;
2014-02-10 01:10:30 +00:00
2018-04-11 13:08:56 +00:00
if ( visible ) {
2018-11-10 02:44:58 +00:00
2018-04-11 13:08:56 +00:00
if ( selected ) {
2018-11-10 02:44:58 +00:00
cw = font - > get_char_size ( fx_char , c [ i + 1 ] ) . x ;
2018-04-11 13:08:56 +00:00
draw_rect ( Rect2 ( p_ofs . x + pofs , p_ofs . y + y , cw , lh ) , selection_bg ) ;
}
2014-02-10 01:10:30 +00:00
2018-04-11 13:08:56 +00:00
if ( p_font_color_shadow . a > 0 ) {
float x_ofs_shadow = align_ofs + pofs ;
float y_ofs_shadow = y + lh - line_descent ;
2019-10-23 05:15:36 +00:00
font - > draw_char ( ci , Point2 ( x_ofs_shadow , y_ofs_shadow ) + shadow_ofs + fx_offset , fx_char , c [ i + 1 ] , p_font_color_shadow ) ;
2018-04-11 13:08:56 +00:00
if ( p_shadow_as_outline ) {
2019-10-23 05:15:36 +00:00
font - > draw_char ( ci , Point2 ( x_ofs_shadow , y_ofs_shadow ) + Vector2 ( - shadow_ofs . x , shadow_ofs . y ) + fx_offset , fx_char , c [ i + 1 ] , p_font_color_shadow ) ;
font - > draw_char ( ci , Point2 ( x_ofs_shadow , y_ofs_shadow ) + Vector2 ( shadow_ofs . x , - shadow_ofs . y ) + fx_offset , fx_char , c [ i + 1 ] , p_font_color_shadow ) ;
font - > draw_char ( ci , Point2 ( x_ofs_shadow , y_ofs_shadow ) + Vector2 ( - shadow_ofs . x , - shadow_ofs . y ) + fx_offset , fx_char , c [ i + 1 ] , p_font_color_shadow ) ;
2018-04-11 13:08:56 +00:00
}
}
2014-02-10 01:10:30 +00:00
2018-04-11 13:08:56 +00:00
if ( selected ) {
2018-11-10 02:44:58 +00:00
drawer . draw_char ( ci , p_ofs + Point2 ( align_ofs + pofs , y + lh - line_descent ) , fx_char , c [ i + 1 ] , override_selected_font_color ? selection_fg : fx_color ) ;
2018-04-11 13:08:56 +00:00
} else {
2018-11-10 02:44:58 +00:00
cw = drawer . draw_char ( ci , p_ofs + Point2 ( align_ofs + pofs , y + lh - line_descent ) + fx_offset , fx_char , c [ i + 1 ] , fx_color ) ;
2018-04-11 13:08:56 +00:00
}
2020-03-29 00:20:07 +00:00
} else if ( previously_visible & & c [ i ] ! = ' \t ' ) {
2020-01-31 05:05:52 +00:00
backtrack + = font - > get_char_size ( fx_char , c [ i + 1 ] ) . x ;
2014-02-10 01:10:30 +00:00
}
2015-06-01 22:42:34 +00:00
p_char_count + + ;
2017-03-05 15:44:50 +00:00
if ( c [ i ] = = ' \t ' ) {
cw = tab_size * font - > get_char_size ( ' ' ) . width ;
2020-03-29 00:20:07 +00:00
backtrack = MAX ( 0 , backtrack - cw ) ;
2015-05-09 21:09:58 +00:00
}
2017-03-05 15:44:50 +00:00
ofs + = cw ;
2014-02-10 01:10:30 +00:00
}
}
2018-01-04 11:10:29 +00:00
if ( underline ) {
Color uc = color ;
uc . a * = 0.5 ;
2018-01-14 17:12:09 +00:00
int uy = y + lh - line_descent + 2 ;
2018-01-04 11:10:29 +00:00
float underline_width = 1.0 ;
# ifdef TOOLS_ENABLED
underline_width * = EDSCALE ;
# endif
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_add_line ( ci , p_ofs + Point2 ( align_ofs + wofs , uy ) , p_ofs + Point2 ( align_ofs + wofs + w , uy ) , uc , underline_width ) ;
2018-09-22 21:41:13 +00:00
} else if ( strikethrough ) {
Color uc = color ;
uc . a * = 0.5 ;
2020-04-06 20:26:58 +00:00
int uy = y + lh - ( line_ascent + line_descent ) / 2 ;
2018-09-22 21:41:13 +00:00
float strikethrough_width = 1.0 ;
# ifdef TOOLS_ENABLED
strikethrough_width * = EDSCALE ;
# endif
2020-03-27 18:21:27 +00:00
RS : : get_singleton ( ) - > canvas_item_add_line ( ci , p_ofs + Point2 ( align_ofs + wofs , uy ) , p_ofs + Point2 ( align_ofs + wofs + w , uy ) , uc , strikethrough_width ) ;
2018-01-04 11:10:29 +00:00
}
2014-02-10 01:10:30 +00:00
}
2015-04-21 19:01:58 +00:00
ADVANCE ( fw ) ;
2014-02-10 01:10:30 +00:00
CHECK_HEIGHT ( fh ) ; //must be done somewhere
2017-03-05 15:44:50 +00:00
c = & c [ end ] ;
2014-02-10 01:10:30 +00:00
}
} break ;
case ITEM_IMAGE : {
2017-03-05 15:44:50 +00:00
lh = 0 ;
if ( p_mode ! = PROCESS_CACHE )
lh = line < l . height_caches . size ( ) ? l . height_caches [ line ] : 1 ;
2015-06-01 22:42:34 +00:00
else
2017-03-05 15:44:50 +00:00
l . char_count + = 1 ; //images count as chars too
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemImage * img = static_cast < ItemImage * > ( it ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Ref < Font > font = _find_font ( it ) ;
2014-02-10 01:10:30 +00:00
if ( font . is_null ( ) )
2017-03-05 15:44:50 +00:00
font = p_base_font ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_POINTER & & r_click_char )
* r_click_char = 0 ;
2014-02-10 01:10:30 +00:00
2019-10-16 10:00:15 +00:00
ENSURE_WIDTH ( img - > size . width ) ;
2014-02-10 01:10:30 +00:00
2019-10-16 10:00:15 +00:00
bool visible = visible_characters < 0 | | ( p_char_count < visible_characters & & YRANGE_VISIBLE ( y + lh - font - > get_descent ( ) - img - > size . height , img - > size . height ) ) ;
2016-10-17 19:03:47 +00:00
if ( visible )
line_is_blank = false ;
2015-06-01 22:42:34 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_DRAW & & visible ) {
2019-10-16 10:00:15 +00:00
img - > image - > draw_rect ( ci , Rect2 ( p_ofs + Point2 ( align_ofs + wofs , y + lh - font - > get_descent ( ) - img - > size . height ) , img - > size ) ) ;
2014-02-10 01:10:30 +00:00
}
2015-06-01 22:42:34 +00:00
p_char_count + + ;
2014-02-10 01:10:30 +00:00
2019-10-16 10:00:15 +00:00
ADVANCE ( img - > size . width ) ;
CHECK_HEIGHT ( ( img - > size . height + font - > get_descent ( ) ) ) ;
2014-02-10 01:10:30 +00:00
} break ;
case ITEM_NEWLINE : {
2017-03-05 15:44:50 +00:00
lh = 0 ;
2020-01-31 05:05:52 +00:00
2016-10-17 19:03:47 +00:00
if ( p_mode ! = PROCESS_CACHE ) {
2017-03-05 15:44:50 +00:00
lh = line < l . height_caches . size ( ) ? l . height_caches [ line ] : 1 ;
2016-10-17 19:03:47 +00:00
line_is_blank = true ;
}
2014-02-10 01:10:30 +00:00
} break ;
2015-12-26 13:25:17 +00:00
case ITEM_TABLE : {
2017-03-05 15:44:50 +00:00
lh = 0 ;
ItemTable * table = static_cast < ItemTable * > ( it ) ;
2020-03-12 12:37:40 +00:00
int hseparation = get_theme_constant ( " table_hseparation " ) ;
int vseparation = get_theme_constant ( " table_vseparation " ) ;
2017-03-05 15:44:50 +00:00
Color ccolor = _find_color ( table , p_base_color ) ;
Vector2 draw_ofs = Point2 ( wofs , y ) ;
2020-03-12 12:37:40 +00:00
Color font_color_shadow = get_theme_color ( " font_color_shadow " ) ;
bool use_outline = get_theme_constant ( " shadow_as_outline " ) ;
Point2 shadow_ofs2 ( get_theme_constant ( " shadow_offset_x " ) , get_theme_constant ( " shadow_offset_y " ) ) ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_CACHE ) {
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
int idx = 0 ;
2015-12-26 13:25:17 +00:00
//set minimums to zero
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < table - > columns . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
table - > columns . write [ i ] . min_width = 0 ;
table - > columns . write [ i ] . max_width = 0 ;
table - > columns . write [ i ] . width = 0 ;
2015-12-26 13:25:17 +00:00
}
//compute minimum width for each cell
2018-01-04 07:07:56 +00:00
const int available_width = p_width - hseparation * ( table - > columns . size ( ) - 1 ) - wofs ;
2017-03-05 15:44:50 +00:00
for ( List < Item * > : : Element * E = table - > subitems . front ( ) ; E ; E = E - > next ( ) ) {
ERR_CONTINUE ( E - > get ( ) - > type ! = ITEM_FRAME ) ; //children should all be frames
ItemFrame * frame = static_cast < ItemFrame * > ( E - > get ( ) ) ;
2015-12-26 13:25:17 +00:00
int column = idx % table - > columns . size ( ) ;
2017-03-05 15:44:50 +00:00
int ly = 0 ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
2015-12-26 13:25:17 +00:00
2019-02-12 20:10:08 +00:00
_process_line ( frame , Point2 ( ) , ly , available_width , i , PROCESS_CACHE , cfont , Color ( ) , font_color_shadow , use_outline , shadow_ofs2 ) ;
2018-07-25 01:11:03 +00:00
table - > columns . write [ column ] . min_width = MAX ( table - > columns [ column ] . min_width , frame - > lines [ i ] . minimum_width ) ;
table - > columns . write [ column ] . max_width = MAX ( table - > columns [ column ] . max_width , frame - > lines [ i ] . maximum_width ) ;
2015-12-26 13:25:17 +00:00
}
idx + + ;
}
2016-10-17 19:03:47 +00:00
//compute available width and total ratio (for expanders)
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
int total_ratio = 0 ;
2018-01-04 07:07:56 +00:00
int remaining_width = available_width ;
2017-03-05 15:44:50 +00:00
table - > total_width = hseparation ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < table - > columns . size ( ) ; i + + ) {
2018-01-04 07:07:56 +00:00
remaining_width - = table - > columns [ i ] . min_width ;
2018-03-18 10:28:17 +00:00
if ( table - > columns [ i ] . max_width > table - > columns [ i ] . min_width )
2018-07-25 01:11:03 +00:00
table - > columns . write [ i ] . expand = true ;
2015-12-26 13:25:17 +00:00
if ( table - > columns [ i ] . expand )
2017-03-05 15:44:50 +00:00
total_ratio + = table - > columns [ i ] . expand_ratio ;
2015-12-26 13:25:17 +00:00
}
//assign actual widths
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < table - > columns . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
table - > columns . write [ i ] . width = table - > columns [ i ] . min_width ;
2019-06-07 10:36:11 +00:00
if ( table - > columns [ i ] . expand & & total_ratio > 0 )
2018-07-25 01:11:03 +00:00
table - > columns . write [ i ] . width + = table - > columns [ i ] . expand_ratio * remaining_width / total_ratio ;
2017-03-05 15:44:50 +00:00
table - > total_width + = table - > columns [ i ] . width + hseparation ;
2015-12-26 13:25:17 +00:00
}
2018-03-24 17:09:35 +00: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 < table - > columns . size ( ) ; 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 ;
2018-07-25 01:11:03 +00:00
table - > columns . write [ i ] . width = table - > columns [ i ] . max_width ;
2018-03-24 17:09:35 +00:00
table - > total_width - = dif ;
total_ratio - = table - > columns [ i ] . expand_ratio ;
}
}
//grow
remaining_width = available_width - table - > total_width ;
if ( remaining_width > 0 & & total_ratio > 0 ) {
for ( int i = 0 ; i < table - > columns . size ( ) ; 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 ) ;
2018-07-25 01:11:03 +00:00
table - > columns . write [ i ] . width + = incr ;
2018-03-24 17:09:35 +00:00
table - > total_width + = incr ;
}
}
}
}
}
2015-12-26 13:25:17 +00:00
//compute caches properly again with the right width
2017-03-05 15:44:50 +00:00
idx = 0 ;
for ( List < Item * > : : Element * E = table - > subitems . front ( ) ; E ; E = E - > next ( ) ) {
ERR_CONTINUE ( E - > get ( ) - > type ! = ITEM_FRAME ) ; //children should all be frames
ItemFrame * frame = static_cast < ItemFrame * > ( E - > get ( ) ) ;
2015-12-26 13:25:17 +00:00
int column = idx % table - > columns . size ( ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
int ly = 0 ;
2019-02-12 20:10:08 +00:00
_process_line ( frame , Point2 ( ) , ly , table - > columns [ column ] . width , i , PROCESS_CACHE , cfont , Color ( ) , font_color_shadow , use_outline , shadow_ofs2 ) ;
2018-07-25 01:11:03 +00:00
frame - > lines . write [ i ] . height_cache = ly ; //actual height
frame - > lines . write [ i ] . height_accum_cache = ly ; //actual height
2015-12-26 13:25:17 +00:00
}
idx + + ;
}
}
2017-03-05 15:44:50 +00:00
Point2 offset ( align_ofs + hseparation , vseparation ) ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
int row_height = 0 ;
2015-12-26 13:25:17 +00:00
//draw using computed caches
2017-03-05 15:44:50 +00:00
int idx = 0 ;
for ( List < Item * > : : Element * E = table - > subitems . front ( ) ; E ; E = E - > next ( ) ) {
ERR_CONTINUE ( E - > get ( ) - > type ! = ITEM_FRAME ) ; //children should all be frames
ItemFrame * frame = static_cast < ItemFrame * > ( E - > get ( ) ) ;
2015-12-26 13:25:17 +00:00
int column = idx % table - > columns . size ( ) ;
2017-03-05 15:44:50 +00:00
int ly = 0 ;
int yofs = 0 ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
int lines_h = frame - > lines [ frame - > lines . size ( ) - 1 ] . height_accum_cache - ( frame - > lines [ 0 ] . height_accum_cache - frame - > lines [ 0 ] . height_cache ) ;
int lines_ofs = p_ofs . y + offset . y + draw_ofs . y ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
bool visible = lines_ofs < get_size ( ) . height & & lines_ofs + lines_h > = 0 ;
2016-10-17 19:03:47 +00:00
if ( visible )
line_is_blank = false ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < frame - > lines . size ( ) ; i + + ) {
2015-12-26 18:18:01 +00:00
if ( visible ) {
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_DRAW ) {
2019-02-12 20:10:08 +00:00
nonblank_line_count + = _process_line ( frame , p_ofs + offset + draw_ofs + Vector2 ( 0 , yofs ) , ly , table - > columns [ column ] . width , i , PROCESS_DRAW , cfont , ccolor , font_color_shadow , use_outline , shadow_ofs2 ) ;
2017-03-05 15:44:50 +00:00
} else if ( p_mode = = PROCESS_POINTER ) {
2019-02-12 20:10:08 +00:00
_process_line ( frame , p_ofs + offset + draw_ofs + Vector2 ( 0 , yofs ) , ly , table - > columns [ column ] . width , i , PROCESS_POINTER , cfont , ccolor , font_color_shadow , use_outline , shadow_ofs2 , p_click_pos , r_click_item , r_click_char , r_outside ) ;
2017-11-05 10:38:14 +00:00
if ( r_click_item & & * r_click_item ) {
RETURN ; // exit early
}
2015-12-26 18:18:01 +00:00
}
2015-12-26 13:25:17 +00:00
}
2015-12-26 18:18:01 +00:00
2017-03-05 15:44:50 +00:00
yofs + = frame - > lines [ i ] . height_cache ;
if ( p_mode = = PROCESS_CACHE ) {
2018-07-25 01:11:03 +00:00
frame - > lines . write [ i ] . height_accum_cache = offset . y + draw_ofs . y + frame - > lines [ i ] . height_cache ;
2015-12-26 13:25:17 +00:00
}
}
2017-03-05 15:44:50 +00:00
row_height = MAX ( yofs , row_height ) ;
offset . x + = table - > columns [ column ] . width + hseparation ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
if ( column = = table - > columns . size ( ) - 1 ) {
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
offset . y + = row_height + vseparation ;
offset . x = hseparation ;
row_height = 0 ;
2015-12-26 13:25:17 +00:00
}
idx + + ;
}
int total_height = offset . y ;
if ( row_height ) {
2017-03-05 15:44:50 +00:00
total_height = row_height + vseparation ;
2015-12-26 13:25:17 +00:00
}
2017-03-05 15:44:50 +00:00
ADVANCE ( table - > total_width ) ;
CHECK_HEIGHT ( total_height ) ;
2015-12-26 13:25:17 +00:00
} break ;
2014-02-10 01:10:30 +00:00
2019-04-09 15:08:36 +00:00
default : {
}
2014-02-10 01:10:30 +00:00
}
Item * itp = it ;
it = _get_next_item ( it ) ;
2017-03-05 15:44:50 +00:00
if ( it & & ( p_line + 1 < p_frame - > lines . size ( ) ) & & p_frame - > lines [ p_line + 1 ] . from = = it ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_mode = = PROCESS_POINTER & & r_click_item & & p_click_pos . y > = p_ofs . y + y & & p_click_pos . y < = p_ofs . y + y + lh ) {
2014-02-10 01:10:30 +00:00
//went to next line, but pointer was on the previous one
2017-03-05 15:44:50 +00:00
if ( r_outside ) * r_outside = true ;
* r_click_item = itp ;
* r_click_char = rchar ;
2016-10-17 19:03:47 +00:00
RETURN ;
2014-02-10 01:10:30 +00:00
}
break ;
}
}
NEW_LINE ;
2016-10-17 19:03:47 +00:00
RETURN ;
# undef RETURN
2014-02-10 01:10:30 +00:00
# undef NEW_LINE
# undef ENSURE_WIDTH
# undef ADVANCE
# undef CHECK_HEIGHT
}
void RichTextLabel : : _scroll_changed ( double ) {
2019-10-21 21:05:10 +00:00
if ( updating_scroll | | ! scroll_active )
2014-02-10 01:10:30 +00:00
return ;
2017-03-05 15:44:50 +00:00
if ( scroll_follow & & vscroll - > get_value ( ) > = ( vscroll - > get_max ( ) - vscroll - > get_page ( ) ) )
scroll_following = true ;
2014-02-10 01:10:30 +00:00
else
2017-03-05 15:44:50 +00:00
scroll_following = false ;
2014-02-10 01:10:30 +00:00
2019-04-27 19:15:07 +00:00
scroll_updated = true ;
2014-02-10 01:10:30 +00:00
update ( ) ;
}
void RichTextLabel : : _update_scroll ( ) {
2018-04-11 13:53:13 +00:00
int total_height = get_content_height ( ) ;
2014-02-10 01:10:30 +00:00
bool exceeds = total_height > get_size ( ) . height & & scroll_active ;
2017-03-05 15:44:50 +00:00
if ( exceeds ! = scroll_visible ) {
2014-02-10 01:10:30 +00:00
if ( exceeds ) {
2017-03-05 15:44:50 +00:00
scroll_visible = true ;
scroll_w = vscroll - > get_combined_minimum_size ( ) . width ;
2014-02-10 01:10:30 +00:00
vscroll - > show ( ) ;
2017-07-06 07:16:27 +00:00
vscroll - > set_anchor_and_margin ( MARGIN_LEFT , ANCHOR_END , - scroll_w ) ;
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
scroll_visible = false ;
scroll_w = 0 ;
2018-12-01 23:06:38 +00:00
vscroll - > hide ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-12-01 23:06:38 +00:00
main - > first_invalid_line = 0 ; //invalidate ALL
_validate_line_caches ( main ) ;
2014-02-10 01:10:30 +00:00
}
}
2018-11-10 02:44:58 +00:00
void RichTextLabel : : _update_fx ( RichTextLabel : : ItemFrame * p_frame , float p_delta_time ) {
Item * it = p_frame ;
while ( it ) {
2020-04-01 23:20:12 +00:00
ItemFX * ifx = nullptr ;
2019-09-25 20:05:42 +00: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 02:44:58 +00:00
if ( ! ifx ) {
it = _get_next_item ( it , true ) ;
continue ;
}
ifx - > elapsed_time + = p_delta_time ;
2020-04-01 23:20:12 +00:00
ItemShake * shake = nullptr ;
2019-09-25 20:05:42 +00:00
if ( it - > type = = ITEM_SHAKE ) {
shake = static_cast < ItemShake * > ( it ) ;
}
2018-11-10 02:44:58 +00: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 01:10:30 +00:00
void RichTextLabel : : _notification ( int p_what ) {
switch ( p_what ) {
2020-01-08 13:01:08 +00:00
case NOTIFICATION_MOUSE_EXIT : {
if ( meta_hovering ) {
2020-04-01 23:20:12 +00:00
meta_hovering = nullptr ;
2020-01-08 13:01:08 +00:00
emit_signal ( " meta_hover_ended " , current_meta ) ;
current_meta = false ;
update ( ) ;
}
} break ;
2014-02-10 01:10:30 +00:00
case NOTIFICATION_RESIZED : {
2017-03-05 15:44:50 +00:00
main - > first_invalid_line = 0 ; //invalidate ALL
2014-02-10 01:10:30 +00:00
update ( ) ;
2015-04-21 19:01:58 +00:00
} break ;
case NOTIFICATION_ENTER_TREE : {
2016-07-17 16:21:44 +00:00
if ( bbcode ! = " " )
set_bbcode ( bbcode ) ;
2017-03-05 15:44:50 +00:00
main - > first_invalid_line = 0 ; //invalidate ALL
2015-04-21 19:01:58 +00:00
update ( ) ;
} break ;
case NOTIFICATION_THEME_CHANGED : {
2019-08-02 02:50:14 +00:00
update ( ) ;
2015-04-21 19:01:58 +00:00
2014-02-10 01:10:30 +00:00
} break ;
case NOTIFICATION_DRAW : {
2015-12-26 13:25:17 +00:00
_validate_line_caches ( main ) ;
2014-02-10 01:10:30 +00:00
_update_scroll ( ) ;
2017-03-05 15:44:50 +00:00
RID ci = get_canvas_item ( ) ;
2017-09-02 16:20:04 +00:00
2014-02-10 01:10:30 +00:00
Size2 size = get_size ( ) ;
2017-09-02 16:20:04 +00:00
Rect2 text_rect = _get_text_rect ( ) ;
2020-03-12 12:37:40 +00:00
draw_style_box ( get_theme_stylebox ( " normal " ) , Rect2 ( Point2 ( ) , size ) ) ;
2014-02-10 01:10:30 +00:00
2014-02-16 00:16:33 +00:00
if ( has_focus ( ) ) {
2020-03-27 18:21:27 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_clip_ignore ( ci , true ) ;
2020-03-12 12:37:40 +00:00
draw_style_box ( get_theme_stylebox ( " focus " ) , Rect2 ( Point2 ( ) , size ) ) ;
2020-03-27 18:21:27 +00:00
RenderingServer : : get_singleton ( ) - > canvas_item_add_clip_ignore ( ci , false ) ;
2014-02-16 00:16:33 +00:00
}
2017-01-04 04:16:14 +00:00
int ofs = vscroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
//todo, change to binary search
int from_line = 0 ;
2015-06-01 22:42:34 +00:00
int total_chars = 0 ;
2017-03-05 15:44:50 +00:00
while ( from_line < main - > lines . size ( ) ) {
2014-02-10 01:10:30 +00:00
2017-09-02 16:20:04 +00:00
if ( main - > lines [ from_line ] . height_accum_cache + _get_text_rect ( ) . get_position ( ) . y > = ofs )
2014-02-10 01:10:30 +00:00
break ;
2017-03-05 15:44:50 +00:00
total_chars + = main - > lines [ from_line ] . char_count ;
2017-09-02 16:20:04 +00:00
from_line + + ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( from_line > = main - > lines . size ( ) )
2014-02-10 01:10:30 +00:00
break ; //nothing to draw
2015-12-26 13:25:17 +00:00
int y = ( main - > lines [ from_line ] . height_accum_cache - main - > lines [ from_line ] . height_cache ) - ofs ;
2020-03-12 12:37:40 +00:00
Ref < Font > base_font = get_theme_font ( " normal_font " ) ;
Color base_color = get_theme_color ( " default_color " ) ;
Color font_color_shadow = get_theme_color ( " font_color_shadow " ) ;
bool use_outline = get_theme_constant ( " shadow_as_outline " ) ;
Point2 shadow_ofs ( get_theme_constant ( " shadow_offset_x " ) , get_theme_constant ( " shadow_offset_y " ) ) ;
2018-04-11 13:08:56 +00:00
2016-10-17 19:03:47 +00:00
visible_line_count = 0 ;
2017-03-05 15:44:50 +00:00
while ( y < size . height & & from_line < main - > lines . size ( ) ) {
2014-02-10 01:10:30 +00:00
2020-04-01 23:20:12 +00:00
visible_line_count + = _process_line ( main , text_rect . get_position ( ) , y , text_rect . get_size ( ) . width - scroll_w , from_line , PROCESS_DRAW , base_font , base_color , font_color_shadow , use_outline , shadow_ofs , Point2i ( ) , nullptr , nullptr , nullptr , total_chars ) ;
2017-03-05 15:44:50 +00:00
total_chars + = main - > lines [ from_line ] . char_count ;
2018-04-11 13:08:56 +00:00
2014-02-10 01:10:30 +00:00
from_line + + ;
}
2018-11-10 02:44:58 +00:00
} break ;
case NOTIFICATION_INTERNAL_PROCESS : {
float dt = get_process_delta_time ( ) ;
_update_fx ( main , dt ) ;
update ( ) ;
2014-02-10 01:10:30 +00:00
}
}
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : _find_click ( ItemFrame * p_frame , const Point2i & p_click , Item * * r_click_item , int * r_click_char , bool * r_outside ) {
2014-02-10 01:10:30 +00:00
if ( r_click_item )
2020-04-01 23:20:12 +00:00
* r_click_item = nullptr ;
2014-02-10 01:10:30 +00:00
2017-09-02 16:20:04 +00:00
Rect2 text_rect = _get_text_rect ( ) ;
2017-01-04 04:16:14 +00:00
int ofs = vscroll - > get_value ( ) ;
2020-03-12 12:37:40 +00:00
Color font_color_shadow = get_theme_color ( " font_color_shadow " ) ;
bool use_outline = get_theme_constant ( " shadow_as_outline " ) ;
Point2 shadow_ofs ( get_theme_constant ( " shadow_offset_x " ) , get_theme_constant ( " shadow_offset_y " ) ) ;
2014-02-10 01:10:30 +00:00
//todo, change to binary search
int from_line = 0 ;
2017-03-05 15:44:50 +00:00
while ( from_line < p_frame - > lines . size ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_frame - > lines [ from_line ] . height_accum_cache > = ofs )
2014-02-10 01:10:30 +00:00
break ;
from_line + + ;
}
2017-03-05 15:44:50 +00:00
if ( from_line > = p_frame - > lines . size ( ) )
2014-02-10 01:10:30 +00:00
return ;
2015-12-26 13:25:17 +00:00
int y = ( p_frame - > lines [ from_line ] . height_accum_cache - p_frame - > lines [ from_line ] . height_cache ) - ofs ;
2020-03-12 12:37:40 +00:00
Ref < Font > base_font = get_theme_font ( " normal_font " ) ;
Color base_color = get_theme_color ( " default_color " ) ;
2014-02-10 01:10:30 +00:00
2017-09-02 16:20:04 +00:00
while ( y < text_rect . get_size ( ) . height & & from_line < p_frame - > lines . size ( ) ) {
2014-02-10 01:10:30 +00:00
2018-04-11 13:08:56 +00:00
_process_line ( p_frame , text_rect . get_position ( ) , y , text_rect . get_size ( ) . width - scroll_w , from_line , PROCESS_POINTER , base_font , base_color , font_color_shadow , use_outline , shadow_ofs , p_click , r_click_item , r_click_char , r_outside ) ;
2014-02-10 01:10:30 +00:00
if ( r_click_item & & * r_click_item )
return ;
from_line + + ;
}
}
2017-03-05 15:44:50 +00:00
Control : : CursorShape RichTextLabel : : get_cursor_shape ( const Point2 & p_pos ) const {
2014-02-10 01:10:30 +00:00
2019-07-26 13:24:02 +00:00
if ( ! underline_meta )
2014-02-10 01:10:30 +00:00
return CURSOR_ARROW ;
2019-07-26 13:24:02 +00:00
if ( selection . click )
return CURSOR_IBEAM ;
2017-03-05 15:44:50 +00:00
if ( main - > first_invalid_line < main - > lines . size ( ) )
2014-02-10 01:10:30 +00:00
return CURSOR_ARROW ; //invalid
2019-04-23 14:10:15 +00:00
int line = 0 ;
2020-04-01 23:20:12 +00:00
Item * item = nullptr ;
2020-01-07 14:46:11 +00:00
bool outside ;
( ( RichTextLabel * ) ( this ) ) - > _find_click ( main , p_pos , & item , & line , & outside ) ;
2019-04-23 14:10:15 +00:00
2020-04-01 23:20:12 +00:00
if ( item & & ! outside & & ( ( RichTextLabel * ) ( this ) ) - > _find_meta ( item , nullptr ) )
2019-04-23 14:10:15 +00:00
return CURSOR_POINTING_HAND ;
return CURSOR_ARROW ;
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
void RichTextLabel : : _gui_input ( Ref < InputEvent > p_event ) {
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseButton > b = p_event ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( b . is_valid ( ) ) {
if ( main - > first_invalid_line < main - > lines . size ( ) )
return ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( b - > get_button_index ( ) = = BUTTON_LEFT ) {
2019-06-26 13:08:25 +00:00
if ( b - > is_pressed ( ) & & ! b - > is_doubleclick ( ) ) {
scroll_updated = false ;
int line = 0 ;
2020-04-01 23:20:12 +00:00
Item * item = nullptr ;
2014-02-10 01:10:30 +00:00
2019-06-26 13:08:25 +00:00
bool outside ;
_find_click ( main , b - > get_position ( ) , & item , & line , & outside ) ;
2014-02-10 01:10:30 +00:00
2019-06-26 13:08:25 +00:00
if ( item ) {
2014-02-10 01:10:30 +00:00
2019-06-26 13:08:25 +00:00
if ( selection . enabled ) {
2014-02-10 01:10:30 +00:00
2019-06-26 13:08:25 +00:00
selection . click = item ;
selection . click_char = line ;
2017-12-04 15:46:20 +00:00
2019-06-26 13:08:25 +00:00
// Erase previous selection.
if ( selection . active ) {
2020-04-01 23:20:12 +00:00
selection . from = nullptr ;
2019-06-26 13:08:25 +00:00
selection . from_char = ' \0 ' ;
2020-04-01 23:20:12 +00:00
selection . to = nullptr ;
2019-06-26 13:08:25 +00:00
selection . to_char = ' \0 ' ;
selection . active = false ;
2017-12-04 15:46:20 +00:00
2019-06-26 13:08:25 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
}
2019-06-26 13:08:25 +00:00
}
} else if ( b - > is_pressed ( ) & & b - > is_doubleclick ( ) & & selection . enabled ) {
2014-02-10 01:10:30 +00:00
2019-06-26 13:08:25 +00:00
//doubleclick: select word
int line = 0 ;
2020-04-01 23:20:12 +00:00
Item * item = nullptr ;
2019-06-26 13:08:25 +00:00
bool outside ;
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
_find_click ( main , b - > get_position ( ) , & item , & line , & outside ) ;
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
while ( item & & item - > type ! = ITEM_TEXT ) {
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
item = _get_next_item ( item , true ) ;
}
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
if ( item & & item - > type = = ITEM_TEXT ) {
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
String itext = static_cast < ItemText * > ( item ) - > text ;
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
int beg , end ;
if ( select_word ( itext , line , beg , end ) ) {
2017-12-23 08:59:54 +00:00
2019-06-26 13:08:25 +00:00
selection . from = item ;
selection . to = item ;
selection . from_char = beg ;
selection . to_char = end - 1 ;
selection . active = true ;
update ( ) ;
2017-12-23 08:59:54 +00:00
}
2019-06-26 13:08:25 +00:00
}
} else if ( ! b - > is_pressed ( ) ) {
2014-02-10 01:10:30 +00:00
2020-04-01 23:20:12 +00:00
selection . click = nullptr ;
2018-10-09 00:04:59 +00:00
2019-06-26 13:08:25 +00:00
if ( ! b - > is_doubleclick ( ) & & ! scroll_updated ) {
int line = 0 ;
2020-04-01 23:20:12 +00:00
Item * item = nullptr ;
2018-10-09 00:04:59 +00:00
2019-06-26 13:08:25 +00:00
bool outside ;
_find_click ( main , b - > get_position ( ) , & item , & line , & outside ) ;
2018-10-09 00:04:59 +00:00
2019-06-26 13:08:25 +00:00
if ( item ) {
2018-10-09 00:04:59 +00:00
2019-06-26 13:08:25 +00:00
Variant meta ;
if ( ! outside & & _find_meta ( item , & meta ) ) {
//meta clicked
2018-10-09 00:04:59 +00:00
2019-06-26 13:08:25 +00:00
emit_signal ( " meta_clicked " , meta ) ;
2018-10-09 00:04:59 +00:00
}
}
2014-02-10 01:10:30 +00:00
}
}
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( b - > get_button_index ( ) = = BUTTON_WHEEL_UP ) {
if ( scroll_active )
vscroll - > set_value ( vscroll - > get_value ( ) - vscroll - > get_page ( ) * b - > get_factor ( ) * 0.5 / 8 ) ;
}
if ( b - > get_button_index ( ) = = BUTTON_WHEEL_DOWN ) {
if ( scroll_active )
vscroll - > set_value ( vscroll - > get_value ( ) + vscroll - > get_page ( ) * b - > get_factor ( ) * 0.5 / 8 ) ;
}
}
2014-02-16 00:16:33 +00:00
2017-11-01 20:49:39 +00:00
Ref < InputEventPanGesture > pan_gesture = p_event ;
if ( pan_gesture . is_valid ( ) ) {
if ( scroll_active )
vscroll - > set_value ( vscroll - > get_value ( ) + vscroll - > get_page ( ) * pan_gesture - > get_delta ( ) . y * 0.5 / 8 ) ;
return ;
}
2017-05-20 15:38:03 +00:00
Ref < InputEventKey > k = p_event ;
if ( k . is_valid ( ) ) {
2017-09-02 17:16:31 +00:00
if ( k - > is_pressed ( ) & & ! k - > get_alt ( ) & & ! k - > get_shift ( ) ) {
2020-02-14 22:15:38 +00:00
bool handled = false ;
2018-04-05 17:59:35 +00:00
switch ( k - > get_keycode ( ) ) {
2017-05-20 15:38:03 +00:00
case KEY_PAGEUP : {
2020-02-14 22:15:38 +00:00
if ( vscroll - > is_visible_in_tree ( ) ) {
2017-05-20 15:38:03 +00:00
vscroll - > set_value ( vscroll - > get_value ( ) - vscroll - > get_page ( ) ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
}
2017-05-20 15:38:03 +00:00
} break ;
case KEY_PAGEDOWN : {
2020-02-14 22:15:38 +00:00
if ( vscroll - > is_visible_in_tree ( ) ) {
2017-05-20 15:38:03 +00:00
vscroll - > set_value ( vscroll - > get_value ( ) + vscroll - > get_page ( ) ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
}
2017-05-20 15:38:03 +00:00
} break ;
case KEY_UP : {
2020-02-14 22:15:38 +00:00
if ( vscroll - > is_visible_in_tree ( ) ) {
2020-03-12 12:37:40 +00:00
vscroll - > set_value ( vscroll - > get_value ( ) - get_theme_font ( " normal_font " ) - > get_height ( ) ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
}
2017-05-20 15:38:03 +00:00
} break ;
case KEY_DOWN : {
2020-02-14 22:15:38 +00:00
if ( vscroll - > is_visible_in_tree ( ) ) {
2020-03-12 12:37:40 +00:00
vscroll - > set_value ( vscroll - > get_value ( ) + get_theme_font ( " normal_font " ) - > get_height ( ) ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
}
2017-05-20 15:38:03 +00:00
} break ;
case KEY_HOME : {
2020-02-14 22:15:38 +00:00
if ( vscroll - > is_visible_in_tree ( ) ) {
2017-05-20 15:38:03 +00:00
vscroll - > set_value ( 0 ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
}
2017-05-20 15:38:03 +00:00
} break ;
case KEY_END : {
2020-02-14 22:15:38 +00:00
if ( vscroll - > is_visible_in_tree ( ) ) {
2017-05-20 15:38:03 +00:00
vscroll - > set_value ( vscroll - > get_max ( ) ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
}
2017-05-20 15:38:03 +00:00
} break ;
case KEY_INSERT :
case KEY_C : {
if ( k - > get_command ( ) ) {
selection_copy ( ) ;
2020-02-14 22:15:38 +00:00
handled = true ;
2017-05-20 15:38:03 +00:00
}
2014-02-16 00:16:33 +00:00
2017-05-20 15:38:03 +00:00
} break ;
2014-02-16 00:16:33 +00:00
}
2017-05-20 15:38:03 +00:00
if ( handled )
accept_event ( ) ;
}
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseMotion > m = p_event ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( m . is_valid ( ) ) {
if ( main - > first_invalid_line < main - > lines . size ( ) )
return ;
2014-02-10 01:10:30 +00:00
2017-11-17 05:49:39 +00:00
int line = 0 ;
2020-04-01 23:20:12 +00:00
Item * item = nullptr ;
2017-11-17 05:49:39 +00:00
bool outside ;
_find_click ( main , m - > get_position ( ) , & item , & line , & outside ) ;
2017-05-20 15:38:03 +00:00
if ( selection . click ) {
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( ! item )
return ; // do not update
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
selection . from = selection . click ;
selection . from_char = selection . click_char ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
selection . to = item ;
selection . to_char = line ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
bool swap = false ;
if ( selection . from - > index > selection . to - > index )
swap = true ;
else if ( selection . from - > index = = selection . to - > index ) {
if ( selection . from_char > selection . to_char )
2017-03-05 15:44:50 +00:00
swap = true ;
2017-05-20 15:38:03 +00:00
else if ( selection . from_char = = selection . to_char ) {
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
selection . active = false ;
return ;
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( swap ) {
SWAP ( selection . from , selection . to ) ;
SWAP ( selection . from_char , selection . to_char ) ;
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
selection . active = true ;
update ( ) ;
}
2017-11-17 05:49:39 +00:00
Variant meta ;
2019-02-21 22:23:05 +00:00
ItemMeta * item_meta ;
if ( item & & ! outside & & _find_meta ( item , & meta , & item_meta ) ) {
if ( meta_hovering ! = item_meta ) {
2017-11-17 05:49:39 +00:00
if ( meta_hovering ) {
emit_signal ( " meta_hover_ended " , current_meta ) ;
}
2019-02-21 22:23:05 +00:00
meta_hovering = item_meta ;
2017-11-17 05:49:39 +00:00
current_meta = meta ;
emit_signal ( " meta_hover_started " , meta ) ;
}
} else if ( meta_hovering ) {
2020-04-01 23:20:12 +00:00
meta_hovering = nullptr ;
2019-04-02 11:59:38 +00:00
emit_signal ( " meta_hover_ended " , current_meta ) ;
2017-11-17 05:49:39 +00:00
current_meta = false ;
}
2014-02-10 01:10:30 +00:00
}
}
Ref < Font > RichTextLabel : : _find_font ( Item * p_item ) {
2017-03-05 15:44:50 +00:00
Item * fontitem = p_item ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( fontitem ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( fontitem - > type = = ITEM_FONT ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemFont * fi = static_cast < ItemFont * > ( fontitem ) ;
2014-02-10 01:10:30 +00:00
return fi - > font ;
}
2017-03-05 15:44:50 +00:00
fontitem = fontitem - > parent ;
2014-02-10 01:10:30 +00:00
}
return Ref < Font > ( ) ;
}
2017-03-05 15:44:50 +00:00
int RichTextLabel : : _find_margin ( Item * p_item , const Ref < Font > & p_base_font ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Item * item = p_item ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int margin = 0 ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( item ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( item - > type = = ITEM_INDENT ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Ref < Font > font = _find_font ( item ) ;
2014-02-10 01:10:30 +00:00
if ( font . is_null ( ) )
2017-03-05 15:44:50 +00:00
font = p_base_font ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemIndent * indent = static_cast < ItemIndent * > ( item ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
margin + = indent - > level * tab_size * font - > get_char_size ( ' ' ) . width ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
} else if ( item - > type = = ITEM_LIST ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Ref < Font > font = _find_font ( item ) ;
2014-02-10 01:10:30 +00:00
if ( font . is_null ( ) )
2017-03-05 15:44:50 +00:00
font = p_base_font ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
item = item - > parent ;
2014-02-10 01:10:30 +00:00
}
return margin ;
}
RichTextLabel : : Align RichTextLabel : : _find_align ( Item * p_item ) {
2017-03-05 15:44:50 +00:00
Item * item = p_item ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( item ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( item - > type = = ITEM_ALIGN ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemAlign * align = static_cast < ItemAlign * > ( item ) ;
2014-02-10 01:10:30 +00:00
return align - > align ;
}
2017-03-05 15:44:50 +00:00
item = item - > parent ;
2014-02-10 01:10:30 +00:00
}
return default_align ;
}
2017-03-05 15:44:50 +00:00
Color RichTextLabel : : _find_color ( Item * p_item , const Color & p_default_color ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Item * item = p_item ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( item ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( item - > type = = ITEM_COLOR ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemColor * color = static_cast < ItemColor * > ( item ) ;
2014-02-10 01:10:30 +00:00
return color - > color ;
}
2017-03-05 15:44:50 +00:00
item = item - > parent ;
2014-02-10 01:10:30 +00:00
}
return p_default_color ;
}
bool RichTextLabel : : _find_underline ( Item * p_item ) {
2017-03-05 15:44:50 +00:00
Item * item = p_item ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( item ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( item - > type = = ITEM_UNDERLINE ) {
2014-02-10 01:10:30 +00:00
return true ;
}
2017-03-05 15:44:50 +00:00
item = item - > parent ;
2014-02-10 01:10:30 +00:00
}
return false ;
}
2018-09-22 21:41:13 +00: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 ;
}
2018-11-10 02:44:58 +00:00
bool RichTextLabel : : _find_by_type ( Item * p_item , ItemType p_type ) {
2019-10-05 17:17:07 +00:00
ERR_FAIL_INDEX_V ( ( int ) p_type , 19 , false ) ;
2018-11-10 02:44:58 +00:00
Item * item = p_item ;
2014-02-10 01:10:30 +00:00
2018-11-10 02:44:58 +00:00
while ( item ) {
if ( item - > type = = p_type ) {
return true ;
}
item = item - > parent ;
}
return false ;
}
2019-09-25 20:05:42 +00: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 02:44:58 +00:00
bool RichTextLabel : : _find_meta ( Item * p_item , Variant * r_meta , ItemMeta * * r_item ) {
2017-03-05 15:44:50 +00:00
Item * item = p_item ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( item ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( item - > type = = ITEM_META ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemMeta * meta = static_cast < ItemMeta * > ( item ) ;
2014-02-10 01:10:30 +00:00
if ( r_meta )
2017-03-05 15:44:50 +00:00
* r_meta = meta - > meta ;
2019-02-21 22:23:05 +00:00
if ( r_item )
* r_item = meta ;
2014-02-10 01:10:30 +00:00
return true ;
}
2017-03-05 15:44:50 +00:00
item = item - > parent ;
2014-02-10 01:10:30 +00:00
}
return false ;
}
2019-06-20 10:42:25 +00:00
bool RichTextLabel : : _find_layout_subitem ( Item * from , Item * to ) {
if ( from & & from ! = to ) {
if ( from - > type ! = ITEM_FONT & & from - > type ! = ITEM_COLOR & & from - > type ! = ITEM_UNDERLINE & & from - > type ! = ITEM_STRIKETHROUGH )
return true ;
for ( List < Item * > : : Element * E = from - > subitems . front ( ) ; E ; E = E - > next ( ) ) {
bool layout = _find_layout_subitem ( E - > get ( ) , to ) ;
if ( layout )
return true ;
}
}
return false ;
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : _validate_line_caches ( ItemFrame * p_frame ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_frame - > first_invalid_line = = p_frame - > lines . size ( ) )
2014-02-10 01:10:30 +00:00
return ;
2016-10-17 19:03:47 +00:00
//validate invalid lines
2014-02-10 01:10:30 +00:00
Size2 size = get_size ( ) ;
2018-07-20 21:14:33 +00:00
if ( fixed_width ! = - 1 ) {
size . width = fixed_width ;
}
2017-09-02 16:20:04 +00:00
Rect2 text_rect = _get_text_rect ( ) ;
2020-03-12 12:37:40 +00:00
Color font_color_shadow = get_theme_color ( " font_color_shadow " ) ;
bool use_outline = get_theme_constant ( " shadow_as_outline " ) ;
Point2 shadow_ofs ( get_theme_constant ( " shadow_offset_x " ) , get_theme_constant ( " shadow_offset_y " ) ) ;
2014-02-10 01:10:30 +00:00
2020-03-12 12:37:40 +00:00
Ref < Font > base_font = get_theme_font ( " normal_font " ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = p_frame - > first_invalid_line ; i < p_frame - > lines . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int y = 0 ;
2018-04-11 13:08:56 +00:00
_process_line ( p_frame , text_rect . get_position ( ) , y , text_rect . get_size ( ) . width - scroll_w , i , PROCESS_CACHE , base_font , Color ( ) , font_color_shadow , use_outline , shadow_ofs ) ;
2018-07-25 01:11:03 +00:00
p_frame - > lines . write [ i ] . height_cache = y ;
p_frame - > lines . write [ i ] . height_accum_cache = y ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( i > 0 )
2018-07-25 01:11:03 +00:00
p_frame - > lines . write [ i ] . height_accum_cache + = p_frame - > lines [ i - 1 ] . height_accum_cache ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
int total_height = 0 ;
2015-12-26 13:25:17 +00:00
if ( p_frame - > lines . size ( ) )
2020-03-12 12:37:40 +00:00
total_height = p_frame - > lines [ p_frame - > lines . size ( ) - 1 ] . height_accum_cache + get_theme_stylebox ( " normal " ) - > get_minimum_size ( ) . height ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
main - > first_invalid_line = p_frame - > lines . size ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
updating_scroll = true ;
2014-02-10 01:10:30 +00:00
vscroll - > set_max ( total_height ) ;
vscroll - > set_page ( size . height ) ;
if ( scroll_follow & & scroll_following )
2017-03-05 15:44:50 +00:00
vscroll - > set_value ( total_height - size . height ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
updating_scroll = false ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : _invalidate_current_line ( ItemFrame * p_frame ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( p_frame - > lines . size ( ) - 1 < = p_frame - > first_invalid_line ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
p_frame - > first_invalid_line = p_frame - > lines . size ( ) - 1 ;
2014-02-10 01:10:30 +00:00
update ( ) ;
}
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : add_text ( const String & p_text ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( current - > type = = ITEM_TABLE )
2015-12-26 13:25:17 +00:00
return ; //can't add anything here
2017-03-05 15:44:50 +00:00
int pos = 0 ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( pos < p_text . length ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int end = p_text . find ( " \n " , pos ) ;
2014-02-10 01:10:30 +00:00
String line ;
2017-03-05 15:44:50 +00:00
bool eol = false ;
if ( end = = - 1 ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
end = p_text . length ( ) ;
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
eol = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( pos = = 0 & & end = = p_text . length ( ) )
line = p_text ;
2014-02-10 01:10:30 +00:00
else
2017-03-05 15:44:50 +00:00
line = p_text . substr ( pos , end - pos ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( line . length ( ) > 0 ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( current - > subitems . size ( ) & & current - > subitems . back ( ) - > get ( ) - > type = = ITEM_TEXT ) {
2014-02-10 01:10:30 +00:00
//append text condition!
2017-03-05 15:44:50 +00:00
ItemText * ti = static_cast < ItemText * > ( current - > subitems . back ( ) - > get ( ) ) ;
ti - > text + = line ;
2015-12-26 13:25:17 +00:00
_invalidate_current_line ( main ) ;
2014-02-10 01:10:30 +00:00
} else {
//append item condition
2017-03-05 15:44:50 +00:00
ItemText * item = memnew ( ItemText ) ;
item - > text = line ;
_add_item ( item , false ) ;
2014-02-10 01:10:30 +00:00
}
}
if ( eol ) {
2017-03-05 15:44:50 +00:00
ItemNewline * item = memnew ( ItemNewline ) ;
item - > line = current_frame - > lines . size ( ) ;
_add_item ( item , false ) ;
current_frame - > lines . resize ( current_frame - > lines . size ( ) + 1 ) ;
if ( item - > type ! = ITEM_NEWLINE )
2018-07-25 01:11:03 +00:00
current_frame - > lines . write [ current_frame - > lines . size ( ) - 1 ] . from = item ;
2015-12-26 13:25:17 +00:00
_invalidate_current_line ( current_frame ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
pos = end + 1 ;
2014-02-10 01:10:30 +00:00
}
}
2015-12-26 13:25:17 +00:00
void RichTextLabel : : _add_item ( Item * p_item , bool p_enter , bool p_ensure_newline ) {
2017-03-05 15:44:50 +00:00
p_item - > parent = current ;
p_item - > E = current - > subitems . push_back ( p_item ) ;
p_item - > index = current_idx + + ;
2015-12-26 13:25:17 +00:00
2014-02-10 01:10:30 +00:00
if ( p_enter )
2017-03-05 15:44:50 +00:00
current = p_item ;
2014-02-10 01:10:30 +00:00
2019-06-14 20:38:33 +00: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 10:42:25 +00:00
if ( _find_layout_subitem ( from , p_item ) ) {
2019-06-14 20:38:33 +00:00
_invalidate_current_line ( current_frame ) ;
current_frame - > lines . resize ( current_frame - > lines . size ( ) + 1 ) ;
}
2015-12-26 13:25:17 +00:00
}
2020-04-01 23:20:12 +00:00
if ( current_frame - > lines [ current_frame - > lines . size ( ) - 1 ] . from = = nullptr ) {
2018-07-25 01:11:03 +00:00
current_frame - > lines . write [ current_frame - > lines . size ( ) - 1 ] . from = p_item ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
p_item - > line = current_frame - > lines . size ( ) - 1 ;
2014-02-10 01:10:30 +00:00
2015-12-26 13:25:17 +00:00
_invalidate_current_line ( current_frame ) ;
2020-03-20 20:51:53 +00:00
if ( fixed_width ! = - 1 ) {
minimum_size_changed ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : _remove_item ( Item * p_item , const int p_line , const int p_subitem_line ) {
2017-01-31 21:57:46 +00:00
int size = p_item - > subitems . size ( ) ;
if ( size = = 0 ) {
p_item - > parent - > subitems . erase ( p_item ) ;
if ( p_item - > type = = ITEM_NEWLINE ) {
current_frame - > lines . remove ( p_line ) ;
for ( int i = p_subitem_line ; i < current - > subitems . size ( ) ; i + + ) {
if ( current - > subitems [ i ] - > line > 0 )
current - > subitems [ i ] - > line - - ;
}
}
2017-03-05 15:44:50 +00:00
} else {
2017-01-31 21:57:46 +00:00
for ( int i = 0 ; i < size ; i + + ) {
_remove_item ( p_item - > subitems . front ( ) - > get ( ) , p_line , p_subitem_line ) ;
}
}
}
2019-06-11 18:43:37 +00:00
void RichTextLabel : : add_image ( const Ref < Texture2D > & p_image , const int p_width , const int p_height ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( current - > type = = ITEM_TABLE )
2015-12-26 13:25:17 +00:00
return ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND ( p_image . is_null ( ) ) ;
2017-03-05 15:44:50 +00:00
ItemImage * item = memnew ( ItemImage ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
item - > image = p_image ;
2019-10-16 10:00:15 +00: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
item - > size . height = p_image - > get_height ( ) ;
item - > size . width = p_image - > get_width ( ) ;
}
}
2017-03-05 15:44:50 +00:00
_add_item ( item , false ) ;
2014-02-10 01:10:30 +00:00
}
void RichTextLabel : : add_newline ( ) {
2017-03-05 15:44:50 +00:00
if ( current - > type = = ITEM_TABLE )
2015-12-26 13:25:17 +00:00
return ;
2017-03-05 15:44:50 +00:00
ItemNewline * item = memnew ( ItemNewline ) ;
item - > line = current_frame - > lines . size ( ) ;
_add_item ( item , false ) ;
2017-11-02 00:46:28 +00:00
current_frame - > lines . resize ( current_frame - > lines . size ( ) + 1 ) ;
_invalidate_current_line ( current_frame ) ;
2014-02-10 01:10:30 +00:00
}
2017-01-31 21:57:46 +00:00
bool RichTextLabel : : remove_line ( const int p_line ) {
if ( p_line > = current_frame - > lines . size ( ) | | p_line < 0 )
return false ;
2018-09-08 18:34:37 +00:00
int i = 0 ;
while ( i < current - > subitems . size ( ) & & current - > subitems [ i ] - > line < p_line ) {
i + + ;
}
2017-01-31 21:57:46 +00:00
2018-09-08 18:34:37 +00:00
bool was_newline = false ;
while ( i < current - > subitems . size ( ) ) {
was_newline = current - > subitems [ i ] - > type = = ITEM_NEWLINE ;
_remove_item ( current - > subitems [ i ] , current - > subitems [ i ] - > line , p_line ) ;
if ( was_newline )
break ;
}
2017-01-31 21:57:46 +00:00
2018-09-08 18:34:37 +00:00
if ( ! was_newline ) {
current_frame - > lines . remove ( p_line ) ;
2019-12-12 08:35:50 +00:00
if ( current_frame - > lines . size ( ) = = 0 ) {
current_frame - > lines . resize ( 1 ) ;
}
2018-09-08 18:34:37 +00:00
}
2017-01-31 21:57:46 +00:00
2018-09-08 18:34:37 +00:00
if ( p_line = = 0 & & current - > subitems . size ( ) > 0 )
2018-07-25 01:11:03 +00:00
main - > lines . write [ 0 ] . from = main ;
2017-01-31 21:57:46 +00:00
main - > first_invalid_line = 0 ;
2018-09-08 18:34:37 +00:00
2017-01-31 21:57:46 +00:00
return true ;
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : push_font ( const Ref < Font > & p_font ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND ( p_font . is_null ( ) ) ;
2017-03-05 15:44:50 +00:00
ItemFont * item = memnew ( ItemFont ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
item - > font = p_font ;
_add_item ( item , true ) ;
2014-02-10 01:10:30 +00:00
}
2018-09-22 21:41:13 +00:00
2019-10-16 13:22:56 +00:00
void RichTextLabel : : push_normal ( ) {
2020-03-12 12:37:40 +00:00
Ref < Font > normal_font = get_theme_font ( " normal_font " ) ;
2019-10-16 13:22:56 +00:00
ERR_FAIL_COND ( normal_font . is_null ( ) ) ;
push_font ( normal_font ) ;
}
void RichTextLabel : : push_bold ( ) {
2020-03-12 12:37:40 +00:00
Ref < Font > bold_font = get_theme_font ( " bold_font " ) ;
2019-10-16 13:22:56 +00:00
ERR_FAIL_COND ( bold_font . is_null ( ) ) ;
push_font ( bold_font ) ;
}
void RichTextLabel : : push_bold_italics ( ) {
2020-03-12 12:37:40 +00:00
Ref < Font > bold_italics_font = get_theme_font ( " bold_italics_font " ) ;
2019-10-16 13:22:56 +00:00
ERR_FAIL_COND ( bold_italics_font . is_null ( ) ) ;
push_font ( bold_italics_font ) ;
}
void RichTextLabel : : push_italics ( ) {
2020-03-12 12:37:40 +00:00
Ref < Font > italics_font = get_theme_font ( " italics_font " ) ;
2019-10-16 13:22:56 +00:00
ERR_FAIL_COND ( italics_font . is_null ( ) ) ;
push_font ( italics_font ) ;
}
void RichTextLabel : : push_mono ( ) {
2020-03-12 12:37:40 +00:00
Ref < Font > mono_font = get_theme_font ( " mono_font " ) ;
2019-10-16 13:22:56 +00:00
ERR_FAIL_COND ( mono_font . is_null ( ) ) ;
push_font ( mono_font ) ;
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : push_color ( const Color & p_color ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemColor * item = memnew ( ItemColor ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
item - > color = p_color ;
_add_item ( item , true ) ;
2014-02-10 01:10:30 +00:00
}
2018-09-22 21:41:13 +00:00
2014-02-10 01:10:30 +00:00
void RichTextLabel : : push_underline ( ) {
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemUnderline * item = memnew ( ItemUnderline ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
_add_item ( item , true ) ;
2014-02-10 01:10:30 +00:00
}
2018-09-22 21:41:13 +00:00
void RichTextLabel : : push_strikethrough ( ) {
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemStrikethrough * item = memnew ( ItemStrikethrough ) ;
_add_item ( item , true ) ;
}
2014-02-10 01:10:30 +00:00
void RichTextLabel : : push_align ( Align p_align ) {
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemAlign * item = memnew ( ItemAlign ) ;
item - > align = p_align ;
_add_item ( item , true , true ) ;
2014-02-10 01:10:30 +00:00
}
void RichTextLabel : : push_indent ( int p_level ) {
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ERR_FAIL_COND ( p_level < 0 ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemIndent * item = memnew ( ItemIndent ) ;
item - > level = p_level ;
_add_item ( item , true , true ) ;
2014-02-10 01:10:30 +00:00
}
void RichTextLabel : : push_list ( ListType p_list ) {
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ERR_FAIL_INDEX ( p_list , 3 ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemList * item = memnew ( ItemList ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
item - > list_type = p_list ;
_add_item ( item , true , true ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : push_meta ( const Variant & p_meta ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type = = ITEM_TABLE ) ;
ItemMeta * item = memnew ( ItemMeta ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
item - > meta = p_meta ;
_add_item ( item , true ) ;
2014-02-10 01:10:30 +00:00
}
2015-12-26 13:25:17 +00:00
void RichTextLabel : : push_table ( int p_columns ) {
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( p_columns < 1 ) ;
ItemTable * item = memnew ( ItemTable ) ;
2015-12-26 13:25:17 +00:00
item - > columns . resize ( p_columns ) ;
2017-03-05 15:44:50 +00:00
item - > total_width = 0 ;
for ( int i = 0 ; i < item - > columns . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
item - > columns . write [ i ] . expand = false ;
item - > columns . write [ i ] . expand_ratio = 1 ;
2015-12-26 13:25:17 +00:00
}
2017-03-05 15:44:50 +00:00
_add_item ( item , true , true ) ;
2015-12-26 13:25:17 +00:00
}
2018-11-10 02:44:58 +00: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 ) ;
}
2019-09-25 20:05:42 +00:00
void RichTextLabel : : push_customfx ( Ref < RichTextEffect > p_custom_effect , Dictionary p_environment ) {
2018-11-10 02:44:58 +00:00
ItemCustomFX * item = memnew ( ItemCustomFX ) ;
2019-09-25 20:05:42 +00:00
item - > custom_effect = p_custom_effect ;
item - > char_fx_transform - > environment = p_environment ;
2018-11-10 02:44:58 +00:00
_add_item ( item , true ) ;
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : set_table_column_expand ( int p_column , bool p_expand , int p_ratio ) {
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type ! = ITEM_TABLE ) ;
ItemTable * table = static_cast < ItemTable * > ( current ) ;
ERR_FAIL_INDEX ( p_column , table - > columns . size ( ) ) ;
2018-07-25 01:11:03 +00:00
table - > columns . write [ p_column ] . expand = p_expand ;
table - > columns . write [ p_column ] . expand_ratio = p_ratio ;
2015-12-26 13:25:17 +00:00
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : push_cell ( ) {
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( current - > type ! = ITEM_TABLE ) ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
ItemFrame * item = memnew ( ItemFrame ) ;
item - > parent_frame = current_frame ;
_add_item ( item , true ) ;
current_frame = item ;
item - > cell = true ;
item - > parent_line = item - > parent_frame - > lines . size ( ) - 1 ;
2015-12-26 13:25:17 +00:00
item - > lines . resize ( 1 ) ;
2020-04-01 23:20:12 +00:00
item - > lines . write [ 0 ] . from = nullptr ;
2017-03-05 15:44:50 +00:00
item - > first_invalid_line = 0 ;
2015-12-26 13:25:17 +00:00
}
int RichTextLabel : : get_current_table_column ( ) const {
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( current - > type ! = ITEM_TABLE , - 1 ) ;
2015-12-26 13:25:17 +00:00
2017-03-05 15:44:50 +00:00
ItemTable * table = static_cast < ItemTable * > ( current ) ;
2015-12-26 13:25:17 +00:00
return table - > subitems . size ( ) % table - > columns . size ( ) ;
}
2014-02-10 01:10:30 +00:00
void RichTextLabel : : pop ( ) {
ERR_FAIL_COND ( ! current - > parent ) ;
2017-03-05 15:44:50 +00:00
if ( current - > type = = ITEM_FRAME ) {
current_frame = static_cast < ItemFrame * > ( current ) - > parent_frame ;
2015-12-26 13:25:17 +00:00
}
2017-03-05 15:44:50 +00:00
current = current - > parent ;
2014-02-10 01:10:30 +00:00
}
void RichTextLabel : : clear ( ) {
main - > _clear_children ( ) ;
2017-03-05 15:44:50 +00:00
current = main ;
current_frame = main ;
2015-12-26 13:25:17 +00:00
main - > lines . clear ( ) ;
main - > lines . resize ( 1 ) ;
2017-03-05 15:44:50 +00:00
main - > first_invalid_line = 0 ;
2014-02-10 01:10:30 +00:00
update ( ) ;
2020-04-01 23:20:12 +00:00
selection . click = nullptr ;
2017-03-05 15:44:50 +00:00
selection . active = false ;
current_idx = 1 ;
2020-03-20 20:51:53 +00:00
if ( fixed_width ! = - 1 ) {
minimum_size_changed ( ) ;
}
2014-02-10 01:10:30 +00:00
}
void RichTextLabel : : set_tab_size ( int p_spaces ) {
2017-03-05 15:44:50 +00:00
tab_size = p_spaces ;
main - > first_invalid_line = 0 ;
2014-02-10 01:10:30 +00:00
update ( ) ;
}
int RichTextLabel : : get_tab_size ( ) const {
return tab_size ;
}
void RichTextLabel : : set_meta_underline ( bool p_underline ) {
2017-03-05 15:44:50 +00:00
underline_meta = p_underline ;
2014-02-10 01:10:30 +00:00
update ( ) ;
}
bool RichTextLabel : : is_meta_underlined ( ) const {
return underline_meta ;
}
2017-09-27 17:24:05 +00: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 01:10:30 +00:00
void RichTextLabel : : set_offset ( int p_pixel ) {
2017-01-04 04:16:14 +00:00
vscroll - > set_value ( p_pixel ) ;
2014-02-10 01:10:30 +00:00
}
void RichTextLabel : : set_scroll_active ( bool p_active ) {
2017-03-05 15:44:50 +00:00
if ( scroll_active = = p_active )
2014-02-10 01:10:30 +00:00
return ;
2017-03-05 15:44:50 +00:00
scroll_active = p_active ;
2014-02-10 01:10:30 +00:00
update ( ) ;
}
bool RichTextLabel : : is_scroll_active ( ) const {
return scroll_active ;
}
void RichTextLabel : : set_scroll_follow ( bool p_follow ) {
2017-03-05 15:44:50 +00:00
scroll_follow = p_follow ;
if ( ! vscroll - > is_visible_in_tree ( ) | | vscroll - > get_value ( ) > = ( vscroll - > get_max ( ) - vscroll - > get_page ( ) ) )
scroll_following = true ;
2014-02-10 01:10:30 +00:00
}
bool RichTextLabel : : is_scroll_following ( ) const {
return scroll_follow ;
}
2017-03-05 15:44:50 +00:00
Error RichTextLabel : : parse_bbcode ( const String & p_bbcode ) {
2014-02-10 01:10:30 +00:00
clear ( ) ;
return append_bbcode ( p_bbcode ) ;
}
2017-03-05 15:44:50 +00:00
Error RichTextLabel : : append_bbcode ( const String & p_bbcode ) {
2014-02-10 01:10:30 +00:00
int pos = 0 ;
List < String > tag_stack ;
2020-03-12 12:37:40 +00:00
Ref < Font > normal_font = get_theme_font ( " normal_font " ) ;
Ref < Font > bold_font = get_theme_font ( " bold_font " ) ;
Ref < Font > italics_font = get_theme_font ( " italics_font " ) ;
Ref < Font > bold_italics_font = get_theme_font ( " bold_italics_font " ) ;
Ref < Font > mono_font = get_theme_font ( " mono_font " ) ;
2015-04-21 19:01:58 +00:00
2020-03-12 12:37:40 +00:00
Color base_color = get_theme_color ( " default_color " ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int indent_level = 0 ;
2015-04-21 19:01:58 +00:00
2017-03-05 15:44:50 +00:00
bool in_bold = false ;
bool in_italics = false ;
2015-04-21 19:01:58 +00:00
2018-11-10 02:44:58 +00:00
set_process_internal ( false ) ;
2017-03-05 15:44:50 +00:00
while ( pos < p_bbcode . length ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int brk_pos = p_bbcode . find ( " [ " , pos ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( brk_pos < 0 )
brk_pos = p_bbcode . length ( ) ;
2014-02-10 01:10:30 +00:00
if ( brk_pos > pos ) {
2017-03-05 15:44:50 +00:00
add_text ( p_bbcode . substr ( pos , brk_pos - pos ) ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( brk_pos = = p_bbcode . length ( ) )
2018-09-22 21:41:13 +00:00
break ; //nothing else to add
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int brk_end = p_bbcode . find ( " ] " , brk_pos + 1 ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( brk_end = = - 1 ) {
2014-02-10 01:10:30 +00:00
//no close, add the rest
2017-03-05 15:44:50 +00:00
add_text ( p_bbcode . substr ( brk_pos , p_bbcode . length ( ) - brk_pos ) ) ;
2014-02-10 01:10:30 +00:00
break ;
}
2017-03-05 15:44:50 +00:00
String tag = p_bbcode . substr ( brk_pos + 1 , brk_end - brk_pos - 1 ) ;
2015-04-21 19:01:58 +00:00
if ( tag . begins_with ( " / " ) & & tag_stack . size ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
bool tag_ok = tag_stack . size ( ) & & tag_stack . front ( ) - > get ( ) = = tag . substr ( 1 , tag . length ( ) ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( tag_stack . front ( ) - > get ( ) = = " b " )
in_bold = false ;
if ( tag_stack . front ( ) - > get ( ) = = " i " )
in_italics = false ;
if ( tag_stack . front ( ) - > get ( ) = = " indent " )
2015-04-21 19:01:58 +00:00
indent_level - - ;
2014-02-10 01:10:30 +00:00
if ( ! tag_ok ) {
2018-11-10 02:44:58 +00:00
add_text ( " [ " + tag ) ;
pos = brk_end ;
2014-02-10 01:10:30 +00:00
continue ;
}
tag_stack . pop_front ( ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
if ( tag ! = " /img " )
2014-02-10 01:10:30 +00:00
pop ( ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " b " ) {
2014-02-10 01:10:30 +00:00
//use bold font
2017-03-05 15:44:50 +00:00
in_bold = true ;
2015-04-21 19:01:58 +00:00
if ( in_italics )
push_font ( bold_italics_font ) ;
else
push_font ( bold_font ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " i " ) {
2014-02-10 01:10:30 +00:00
//use italics font
2017-03-05 15:44:50 +00:00
in_italics = true ;
2015-04-21 19:01:58 +00:00
if ( in_bold )
push_font ( bold_italics_font ) ;
else
push_font ( italics_font ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " code " ) {
2014-02-10 01:10:30 +00:00
//use monospace font
2015-04-21 19:01:58 +00:00
push_font ( mono_font ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2015-12-26 13:25:17 +00:00
} else if ( tag . begins_with ( " table= " ) ) {
2017-03-05 15:44:50 +00:00
int columns = tag . substr ( 6 , tag . length ( ) ) . to_int ( ) ;
if ( columns < 1 )
columns = 1 ;
2018-09-22 21:41:13 +00:00
2015-12-26 13:25:17 +00:00
push_table ( columns ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-12-26 13:25:17 +00:00
tag_stack . push_front ( " table " ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " cell " ) {
2015-12-26 13:25:17 +00:00
push_cell ( ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-12-26 13:25:17 +00:00
tag_stack . push_front ( tag ) ;
} else if ( tag . begins_with ( " cell= " ) ) {
2018-03-18 10:28:17 +00:00
int ratio = tag . substr ( 5 , tag . length ( ) ) . to_int ( ) ;
2017-03-05 15:44:50 +00:00
if ( ratio < 1 )
ratio = 1 ;
2018-09-22 21:41:13 +00:00
2017-03-05 15:44:50 +00:00
set_table_column_expand ( get_current_table_column ( ) , true , ratio ) ;
2015-12-26 13:25:17 +00:00
push_cell ( ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-12-26 13:25:17 +00:00
tag_stack . push_front ( " cell " ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " u " ) {
2014-02-10 01:10:30 +00:00
//use underline
push_underline ( ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " s " ) {
2014-02-10 01:10:30 +00:00
2018-09-22 21:41:13 +00:00
//use strikethrough
push_strikethrough ( ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " center " ) {
2015-04-21 19:01:58 +00:00
push_align ( ALIGN_CENTER ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-04-21 19:01:58 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " fill " ) {
2015-04-21 19:01:58 +00:00
push_align ( ALIGN_FILL ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-04-21 19:01:58 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " right " ) {
2015-04-21 19:01:58 +00:00
push_align ( ALIGN_RIGHT ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-04-21 19:01:58 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " ul " ) {
2015-04-21 19:01:58 +00:00
push_list ( LIST_DOTS ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-04-21 19:01:58 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " ol " ) {
2015-04-21 19:01:58 +00:00
push_list ( LIST_NUMBERS ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-04-21 19:01:58 +00:00
tag_stack . push_front ( tag ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " indent " ) {
2015-04-21 19:01:58 +00:00
indent_level + + ;
push_indent ( indent_level ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2015-04-21 19:01:58 +00:00
tag_stack . push_front ( tag ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
} else if ( tag = = " url " ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int end = p_bbcode . find ( " [ " , brk_end ) ;
if ( end = = - 1 )
end = p_bbcode . length ( ) ;
String url = p_bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
2014-02-10 01:10:30 +00:00
push_meta ( url ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2015-04-21 19:01:58 +00:00
2014-02-10 01:10:30 +00:00
} else if ( tag . begins_with ( " url= " ) ) {
2017-03-05 15:44:50 +00:00
String url = tag . substr ( 4 , tag . length ( ) ) ;
2014-02-10 01:10:30 +00:00
push_meta ( url ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( " url " ) ;
2017-03-05 15:44:50 +00:00
} else if ( tag = = " img " ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int end = p_bbcode . find ( " [ " , brk_end ) ;
if ( end = = - 1 )
end = p_bbcode . length ( ) ;
2019-10-16 10:00:15 +00:00
2017-03-05 15:44:50 +00:00
String image = p_bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
2014-02-10 01:10:30 +00:00
2019-06-11 18:43:37 +00:00
Ref < Texture2D > texture = ResourceLoader : : load ( image , " Texture2D " ) ;
2014-02-10 01:10:30 +00:00
if ( texture . is_valid ( ) )
add_image ( texture ) ;
2017-03-05 15:44:50 +00:00
pos = end ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( tag ) ;
2019-10-16 10:00:15 +00:00
} else if ( tag . begins_with ( " img= " ) ) {
int width = 0 ;
int height = 0 ;
String params = tag . substr ( 4 , tag . length ( ) ) ;
int sep = params . find ( " x " ) ;
if ( sep = = - 1 ) {
width = params . to_int ( ) ;
} else {
width = params . substr ( 0 , sep ) . to_int ( ) ;
height = params . substr ( sep + 1 , params . length ( ) ) . to_int ( ) ;
}
int end = p_bbcode . find ( " [ " , brk_end ) ;
if ( end = = - 1 )
end = p_bbcode . length ( ) ;
String image = p_bbcode . substr ( brk_end + 1 , end - brk_end - 1 ) ;
2019-06-11 18:43:37 +00:00
Ref < Texture2D > texture = ResourceLoader : : load ( image , " Texture " ) ;
2019-10-16 10:00:15 +00:00
if ( texture . is_valid ( ) )
add_image ( texture , width , height ) ;
pos = end ;
tag_stack . push_front ( " img " ) ;
2014-02-10 01:10:30 +00:00
} else if ( tag . begins_with ( " color= " ) ) {
2017-03-05 15:44:50 +00:00
String col = tag . substr ( 6 , tag . length ( ) ) ;
2014-02-10 01:10:30 +00:00
Color color ;
if ( col . begins_with ( " # " ) )
2017-03-05 15:44:50 +00:00
color = Color : : html ( col ) ;
else if ( col = = " aqua " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 1 , 1 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " black " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 0 , 0 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " blue " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 0 , 1 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " fuchsia " )
2019-07-08 18:03:06 +00:00
color = Color ( 1 , 0 , 1 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " gray " | | col = = " grey " )
2019-07-08 18:03:06 +00:00
color = Color ( 0.5 , 0.5 , 0.5 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " green " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 0.5 , 0 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " lime " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 1 , 0 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " maroon " )
2019-07-08 18:03:06 +00:00
color = Color ( 0.5 , 0 , 0 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " navy " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 0 , 0.5 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " olive " )
2019-07-08 18:03:06 +00:00
color = Color ( 0.5 , 0.5 , 0 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " purple " )
2019-07-08 18:03:06 +00:00
color = Color ( 0.5 , 0 , 0.5 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " red " )
2019-07-08 18:03:06 +00:00
color = Color ( 1 , 0 , 0 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " silver " )
2019-07-08 18:03:06 +00:00
color = Color ( 0.75 , 0.75 , 0.75 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " teal " )
2019-07-08 18:03:06 +00:00
color = Color ( 0 , 0.5 , 0.5 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " white " )
2019-07-08 18:03:06 +00:00
color = Color ( 1 , 1 , 1 ) ;
2017-03-05 15:44:50 +00:00
else if ( col = = " yellow " )
2019-07-08 18:03:06 +00:00
color = Color ( 1 , 1 , 0 ) ;
2014-02-10 01:10:30 +00:00
else
2017-03-05 15:44:50 +00:00
color = base_color ;
2014-02-10 01:10:30 +00:00
push_color ( color ) ;
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( " color " ) ;
} else if ( tag . begins_with ( " font= " ) ) {
2017-03-05 15:44:50 +00:00
String fnt = tag . substr ( 5 , tag . length ( ) ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Ref < Font > font = ResourceLoader : : load ( fnt , " Font " ) ;
2014-02-10 01:10:30 +00:00
if ( font . is_valid ( ) )
push_font ( font ) ;
else
2015-04-21 19:01:58 +00:00
push_font ( normal_font ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
pos = brk_end + 1 ;
2014-02-10 01:10:30 +00:00
tag_stack . push_front ( " font " ) ;
2018-11-10 02:44:58 +00:00
} else if ( tag . begins_with ( " fade " ) ) {
Vector < String > tags = tag . split ( " " , false ) ;
int startIndex = 0 ;
int length = 10 ;
if ( tags . size ( ) > 1 ) {
tags . remove ( 0 ) ;
for ( int i = 0 ; i < tags . size ( ) ; i + + ) {
String expr = tags [ i ] ;
if ( expr . begins_with ( " start= " ) ) {
String start_str = expr . substr ( 6 , expr . length ( ) ) ;
startIndex = start_str . to_int ( ) ;
} else if ( expr . begins_with ( " length= " ) ) {
String end_str = expr . substr ( 7 , expr . length ( ) ) ;
length = end_str . to_int ( ) ;
}
}
}
push_fade ( startIndex , length ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " fade " ) ;
} else if ( tag . begins_with ( " shake " ) ) {
Vector < String > tags = tag . split ( " " , false ) ;
int strength = 5 ;
float rate = 20.0f ;
if ( tags . size ( ) > 1 ) {
tags . remove ( 0 ) ;
for ( int i = 0 ; i < tags . size ( ) ; i + + ) {
String expr = tags [ i ] ;
if ( expr . begins_with ( " level= " ) ) {
String str_str = expr . substr ( 6 , expr . length ( ) ) ;
strength = str_str . to_int ( ) ;
} else if ( expr . begins_with ( " rate= " ) ) {
String rate_str = expr . substr ( 5 , expr . length ( ) ) ;
rate = rate_str . to_float ( ) ;
}
}
}
push_shake ( strength , rate ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " shake " ) ;
set_process_internal ( true ) ;
} else if ( tag . begins_with ( " wave " ) ) {
Vector < String > tags = tag . split ( " " , false ) ;
float amplitude = 20.0f ;
float period = 5.0f ;
if ( tags . size ( ) > 1 ) {
tags . remove ( 0 ) ;
for ( int i = 0 ; i < tags . size ( ) ; i + + ) {
String expr = tags [ i ] ;
if ( expr . begins_with ( " amp= " ) ) {
String amp_str = expr . substr ( 4 , expr . length ( ) ) ;
amplitude = amp_str . to_float ( ) ;
} else if ( expr . begins_with ( " freq= " ) ) {
String period_str = expr . substr ( 5 , expr . length ( ) ) ;
period = period_str . to_float ( ) ;
}
}
}
2014-02-10 01:10:30 +00:00
2018-11-10 02:44:58 +00:00
push_wave ( period , amplitude ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " wave " ) ;
set_process_internal ( true ) ;
} else if ( tag . begins_with ( " tornado " ) ) {
Vector < String > tags = tag . split ( " " , false ) ;
float radius = 10.0f ;
float frequency = 1.0f ;
if ( tags . size ( ) > 1 ) {
tags . remove ( 0 ) ;
for ( int i = 0 ; i < tags . size ( ) ; i + + ) {
String expr = tags [ i ] ;
if ( expr . begins_with ( " radius= " ) ) {
String amp_str = expr . substr ( 7 , expr . length ( ) ) ;
radius = amp_str . to_float ( ) ;
} else if ( expr . begins_with ( " freq= " ) ) {
String period_str = expr . substr ( 5 , expr . length ( ) ) ;
frequency = period_str . to_float ( ) ;
}
}
}
push_tornado ( frequency , radius ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " tornado " ) ;
set_process_internal ( true ) ;
} else if ( tag . begins_with ( " rainbow " ) ) {
Vector < String > tags = tag . split ( " " , false ) ;
float saturation = 0.8f ;
float value = 0.8f ;
float frequency = 1.0f ;
if ( tags . size ( ) > 1 ) {
tags . remove ( 0 ) ;
for ( int i = 0 ; i < tags . size ( ) ; i + + ) {
String expr = tags [ i ] ;
if ( expr . begins_with ( " sat= " ) ) {
String sat_str = expr . substr ( 4 , expr . length ( ) ) ;
saturation = sat_str . to_float ( ) ;
} else if ( expr . begins_with ( " val= " ) ) {
String val_str = expr . substr ( 4 , expr . length ( ) ) ;
value = val_str . to_float ( ) ;
} else if ( expr . begins_with ( " freq= " ) ) {
String freq_str = expr . substr ( 5 , expr . length ( ) ) ;
frequency = freq_str . to_float ( ) ;
}
}
}
push_rainbow ( saturation , value , frequency ) ;
pos = brk_end + 1 ;
tag_stack . push_front ( " rainbow " ) ;
set_process_internal ( true ) ;
} else {
Vector < String > expr = tag . split ( " " , false ) ;
if ( expr . size ( ) < 1 ) {
add_text ( " [ " ) ;
pos = brk_pos + 1 ;
} else {
String identifier = expr [ 0 ] ;
expr . remove ( 0 ) ;
Dictionary properties = parse_expressions_for_values ( expr ) ;
Ref < RichTextEffect > effect = _get_custom_effect_by_code ( identifier ) ;
if ( ! effect . is_null ( ) ) {
2019-09-25 20:05:42 +00:00
push_customfx ( effect , properties ) ;
2018-11-10 02:44:58 +00: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 01:10:30 +00:00
}
}
return OK ;
}
void RichTextLabel : : scroll_to_line ( int p_line ) {
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX ( p_line , main - > lines . size ( ) ) ;
2015-12-26 13:25:17 +00:00
_validate_line_caches ( main ) ;
2017-03-05 15:44:50 +00:00
vscroll - > set_value ( main - > lines [ p_line ] . height_accum_cache - main - > lines [ p_line ] . height_cache ) ;
2014-02-10 01:10:30 +00:00
}
int RichTextLabel : : get_line_count ( ) const {
2015-12-26 13:25:17 +00:00
return current_frame - > lines . size ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-10-17 19:03:47 +00:00
int RichTextLabel : : get_visible_line_count ( ) const {
if ( ! is_visible ( ) )
return 0 ;
return visible_line_count ;
}
2014-02-10 01:10:30 +00:00
void RichTextLabel : : set_selection_enabled ( bool p_enabled ) {
2017-03-05 15:44:50 +00:00
selection . enabled = p_enabled ;
2014-02-10 01:10:30 +00:00
if ( ! p_enabled ) {
if ( selection . active ) {
2017-03-05 15:44:50 +00:00
selection . active = false ;
2014-02-10 01:10:30 +00:00
update ( ) ;
}
2014-02-16 00:16:33 +00:00
set_focus_mode ( FOCUS_NONE ) ;
} else {
set_focus_mode ( FOCUS_ALL ) ;
2014-02-10 01:10:30 +00:00
}
}
2017-12-22 18:09:31 +00:00
bool RichTextLabel : : search ( const String & p_string , bool p_from_selection , bool p_search_previous ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( ! selection . enabled , false ) ;
Item * it = main ;
int charidx = 0 ;
2014-02-10 01:10:30 +00:00
2019-04-08 09:03:37 +00:00
if ( p_from_selection & & selection . active ) {
2017-03-05 15:44:50 +00:00
it = selection . to ;
charidx = selection . to_char + 1 ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
while ( it ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( it - > type = = ITEM_TEXT ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ItemText * t = static_cast < ItemText * > ( it ) ;
2020-03-24 19:03:09 +00:00
int sp = t - > text . findn ( p_string , charidx ) ;
2017-03-05 15:44:50 +00:00
if ( sp ! = - 1 ) {
selection . from = it ;
selection . from_char = sp ;
selection . to = it ;
selection . to_char = sp + p_string . length ( ) - 1 ;
selection . active = true ;
2014-02-10 01:10:30 +00:00
update ( ) ;
2015-12-26 13:25:17 +00:00
_validate_line_caches ( main ) ;
2014-02-10 01:10:30 +00:00
2020-03-12 12:37:40 +00:00
int fh = _find_font ( t ) . is_valid ( ) ? _find_font ( t ) - > get_height ( ) : get_theme_font ( " normal_font " ) - > get_height ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
float offset = 0 ;
2014-02-10 01:10:30 +00:00
2015-12-26 13:25:17 +00:00
int line = t - > line ;
2017-03-05 15:44:50 +00:00
Item * item = t ;
while ( item ) {
if ( item - > type = = ITEM_FRAME ) {
ItemFrame * frame = static_cast < ItemFrame * > ( item ) ;
if ( line > = 0 & & line < frame - > lines . size ( ) ) {
offset + = frame - > lines [ line ] . height_accum_cache - frame - > lines [ line ] . height_cache ;
line = frame - > line ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
item = item - > parent ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
vscroll - > set_value ( offset - fh ) ;
2014-02-10 01:10:30 +00:00
return true ;
}
}
2017-12-22 18:09:31 +00:00
if ( p_search_previous )
it = _get_prev_item ( it , true ) ;
else
it = _get_next_item ( it , true ) ;
2017-03-05 15:44:50 +00:00
charidx = 0 ;
2014-02-10 01:10:30 +00:00
}
return false ;
}
2014-02-16 00:16:33 +00:00
void RichTextLabel : : selection_copy ( ) {
2017-12-10 15:08:24 +00:00
if ( ! selection . active | | ! selection . enabled )
2014-02-16 00:16:33 +00:00
return ;
String text ;
2017-03-05 15:44:50 +00:00
RichTextLabel : : Item * item = selection . from ;
2014-02-16 00:16:33 +00:00
2017-03-05 15:44:50 +00:00
while ( item ) {
2014-02-16 00:16:33 +00:00
2017-03-05 15:44:50 +00:00
if ( item - > type = = ITEM_TEXT ) {
2014-02-16 00:16:33 +00:00
2017-03-05 15:44:50 +00:00
String itext = static_cast < ItemText * > ( item ) - > text ;
if ( item = = selection . from & & item = = selection . to ) {
text + = itext . substr ( selection . from_char , selection . to_char - selection . from_char + 1 ) ;
} else if ( item = = selection . from ) {
text + = itext . substr ( selection . from_char , itext . size ( ) ) ;
} else if ( item = = selection . to ) {
text + = itext . substr ( 0 , selection . to_char + 1 ) ;
2014-02-16 00:16:33 +00:00
} else {
2017-03-05 15:44:50 +00:00
text + = itext ;
2014-02-16 00:16:33 +00:00
}
2017-03-05 15:44:50 +00:00
} else if ( item - > type = = ITEM_NEWLINE ) {
text + = " \n " ;
2014-02-16 00:16:33 +00:00
}
2017-03-05 15:44:50 +00:00
if ( item = = selection . to )
2014-02-16 00:16:33 +00:00
break ;
2017-03-05 15:44:50 +00:00
item = _get_next_item ( item , true ) ;
2014-02-16 00:16:33 +00:00
}
2017-03-05 15:44:50 +00:00
if ( text ! = " " ) {
2020-03-03 13:36:29 +00:00
DisplayServer : : get_singleton ( ) - > clipboard_set ( text ) ;
2014-02-16 00:16:33 +00:00
}
}
2014-02-10 01:10:30 +00:00
bool RichTextLabel : : is_selection_enabled ( ) const {
return selection . enabled ;
}
2017-03-05 15:44:50 +00:00
void RichTextLabel : : set_bbcode ( const String & p_bbcode ) {
bbcode = p_bbcode ;
2015-04-21 19:01:58 +00:00
if ( is_inside_tree ( ) & & use_bbcode )
parse_bbcode ( p_bbcode ) ;
2016-07-09 12:26:49 +00:00
else { // raw text
clear ( ) ;
add_text ( p_bbcode ) ;
}
2015-04-21 19:01:58 +00:00
}
2014-02-10 01:10:30 +00:00
2015-04-21 19:01:58 +00:00
String RichTextLabel : : get_bbcode ( ) const {
return bbcode ;
}
void RichTextLabel : : set_use_bbcode ( bool p_enable ) {
2017-03-05 15:44:50 +00:00
if ( use_bbcode = = p_enable )
2015-04-21 19:01:58 +00:00
return ;
2017-03-05 15:44:50 +00:00
use_bbcode = p_enable ;
2016-07-09 12:26:49 +00:00
set_bbcode ( bbcode ) ;
2015-04-21 19:01:58 +00:00
}
bool RichTextLabel : : is_using_bbcode ( ) const {
return use_bbcode ;
}
2016-06-29 15:43:51 +00:00
String RichTextLabel : : get_text ( ) {
String text = " " ;
Item * it = main ;
while ( it ) {
if ( it - > type = = ITEM_TEXT ) {
2017-03-05 15:44:50 +00:00
ItemText * t = static_cast < ItemText * > ( it ) ;
2016-06-29 15:43:51 +00:00
text + = t - > text ;
} else if ( it - > type = = ITEM_NEWLINE ) {
text + = " \n " ;
} else if ( it - > type = = ITEM_INDENT ) {
text + = " \t " ;
}
2017-03-05 15:44:50 +00:00
it = _get_next_item ( it , true ) ;
2016-06-29 15:43:51 +00:00
}
return text ;
}
2017-07-03 19:59:58 +00:00
void RichTextLabel : : set_text ( const String & p_string ) {
clear ( ) ;
add_text ( p_string ) ;
}
void RichTextLabel : : set_percent_visible ( float 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 ;
}
update ( ) ;
}
float RichTextLabel : : get_percent_visible ( ) const {
return percent_visible ;
}
2018-11-10 02:44:58 +00:00
void RichTextLabel : : set_effects ( const Vector < Variant > & effects ) {
custom_effects . clear ( ) ;
for ( int i = 0 ; i < effects . size ( ) ; i + + ) {
Ref < RichTextEffect > effect = Ref < RichTextEffect > ( effects [ i ] ) ;
custom_effects . push_back ( effect ) ;
}
parse_bbcode ( bbcode ) ;
}
Vector < Variant > RichTextLabel : : get_effects ( ) {
Vector < Variant > r ;
for ( int i = 0 ; i < custom_effects . size ( ) ; i + + ) {
2020-02-13 19:03:10 +00:00
r . push_back ( custom_effects [ i ] ) ;
2018-11-10 02:44:58 +00:00
}
return r ;
}
void RichTextLabel : : install_effect ( const Variant effect ) {
Ref < RichTextEffect > rteffect ;
rteffect = effect ;
if ( rteffect . is_valid ( ) ) {
custom_effects . push_back ( effect ) ;
parse_bbcode ( bbcode ) ;
}
}
2018-04-11 13:53:13 +00:00
int RichTextLabel : : get_content_height ( ) {
int total_height = 0 ;
if ( main - > lines . size ( ) )
2020-03-12 12:37:40 +00:00
total_height = main - > lines [ main - > lines . size ( ) - 1 ] . height_accum_cache + get_theme_stylebox ( " normal " ) - > get_minimum_size ( ) . height ;
2018-04-11 13:53:13 +00:00
return total_height ;
}
2014-02-10 01:10:30 +00:00
void RichTextLabel : : _bind_methods ( ) {
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " _gui_input " ) , & RichTextLabel : : _gui_input ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text " ) , & RichTextLabel : : get_text ) ;
ClassDB : : bind_method ( D_METHOD ( " add_text " , " text " ) , & RichTextLabel : : add_text ) ;
2017-07-03 19:59:58 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text " , " text " ) , & RichTextLabel : : set_text ) ;
2019-10-16 10:00:15 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_image " , " image " , " width " , " height " ) , & RichTextLabel : : add_image , DEFVAL ( 0 ) , DEFVAL ( 0 ) ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " newline " ) , & RichTextLabel : : add_newline ) ;
2017-07-22 10:11:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_line " , " line " ) , & RichTextLabel : : remove_line ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " push_font " , " font " ) , & RichTextLabel : : push_font ) ;
2019-10-16 13:22:56 +00: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 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " push_color " , " color " ) , & RichTextLabel : : push_color ) ;
ClassDB : : bind_method ( D_METHOD ( " push_align " , " align " ) , & RichTextLabel : : push_align ) ;
ClassDB : : bind_method ( D_METHOD ( " push_indent " , " level " ) , & RichTextLabel : : push_indent ) ;
ClassDB : : bind_method ( D_METHOD ( " push_list " , " type " ) , & RichTextLabel : : push_list ) ;
ClassDB : : bind_method ( D_METHOD ( " push_meta " , " data " ) , & RichTextLabel : : push_meta ) ;
ClassDB : : bind_method ( D_METHOD ( " push_underline " ) , & RichTextLabel : : push_underline ) ;
2018-09-22 21:41:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " push_strikethrough " ) , & RichTextLabel : : push_strikethrough ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " push_table " , " columns " ) , & RichTextLabel : : push_table ) ;
ClassDB : : bind_method ( D_METHOD ( " set_table_column_expand " , " column " , " expand " , " ratio " ) , & RichTextLabel : : set_table_column_expand ) ;
ClassDB : : bind_method ( D_METHOD ( " push_cell " ) , & RichTextLabel : : push_cell ) ;
ClassDB : : bind_method ( D_METHOD ( " pop " ) , & RichTextLabel : : pop ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " clear " ) , & RichTextLabel : : clear ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-09-27 17:24:05 +00: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 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 15:45:53 +00:00
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll " ) , & RichTextLabel : : get_v_scroll ) ;
2015-11-27 23:11:49 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " scroll_to_line " , " line " ) , & RichTextLabel : : scroll_to_line ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 04:22:15 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " parse_bbcode " , " bbcode " ) , & RichTextLabel : : parse_bbcode ) ;
ClassDB : : bind_method ( D_METHOD ( " append_bbcode " , " bbcode " ) , & RichTextLabel : : append_bbcode ) ;
2015-04-21 19:01:58 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_bbcode " , " text " ) , & RichTextLabel : : set_bbcode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_bbcode " ) , & RichTextLabel : : get_bbcode ) ;
2015-06-01 22:42:34 +00:00
2017-03-05 15:44:50 +00: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-01 22:42:34 +00:00
2017-07-03 19:59:58 +00: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 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_total_character_count " ) , & RichTextLabel : : get_total_character_count ) ;
2015-04-21 19:01:58 +00:00
2017-03-05 15:44:50 +00: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 04:22:15 +00:00
2016-10-17 19:03:47 +00: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 ) ;
2018-04-11 13:53:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_content_height " ) , & RichTextLabel : : get_content_height ) ;
2018-11-10 02:44:58 +00: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 15:44:50 +00:00
ADD_GROUP ( " BBCode " , " bbcode_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " bbcode_enabled " ) , " set_use_bbcode " , " is_using_bbcode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " bbcode_text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_bbcode " , " get_bbcode " ) ;
2017-07-03 19:59:58 +00:00
2017-03-05 15:44:50 +00: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 18:20:53 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " percent_visible " , PROPERTY_HINT_RANGE , " 0,1,0.001 " ) , " set_percent_visible " , " get_percent_visible " ) ;
2018-01-11 22:35:12 +00: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 07:09:41 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2018-01-11 22:35:12 +00: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 17:24:05 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " override_selected_font_color " ) , " set_override_selected_font_color " , " is_overriding_selected_font_color " ) ;
2014-02-10 01:10:30 +00:00
2019-09-11 23:33:36 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " custom_effects " , PROPERTY_HINT_RESOURCE_TYPE , " 17/17:RichTextEffect " , ( PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE ) , " RichTextEffect " ) , " set_effects " , " get_effects " ) ;
2018-11-10 02:44:58 +00:00
2017-10-05 11:44:00 +00:00
ADD_SIGNAL ( MethodInfo ( " meta_clicked " , PropertyInfo ( Variant : : NIL , " meta " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
2017-11-17 05:49:39 +00: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 01:10:30 +00:00
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( ALIGN_LEFT ) ;
BIND_ENUM_CONSTANT ( ALIGN_CENTER ) ;
BIND_ENUM_CONSTANT ( ALIGN_RIGHT ) ;
BIND_ENUM_CONSTANT ( ALIGN_FILL ) ;
BIND_ENUM_CONSTANT ( LIST_NUMBERS ) ;
BIND_ENUM_CONSTANT ( LIST_LETTERS ) ;
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 ) ;
BIND_ENUM_CONSTANT ( ITEM_COLOR ) ;
BIND_ENUM_CONSTANT ( ITEM_UNDERLINE ) ;
2018-09-22 21:41:13 +00:00
BIND_ENUM_CONSTANT ( ITEM_STRIKETHROUGH ) ;
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( ITEM_ALIGN ) ;
BIND_ENUM_CONSTANT ( ITEM_INDENT ) ;
BIND_ENUM_CONSTANT ( ITEM_LIST ) ;
2017-10-21 18:58:02 +00:00
BIND_ENUM_CONSTANT ( ITEM_TABLE ) ;
2018-11-10 02:44:58 +00: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 ) ;
BIND_ENUM_CONSTANT ( ITEM_CUSTOMFX ) ;
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( ITEM_META ) ;
2014-02-10 01:10:30 +00:00
}
2015-06-01 22:42:34 +00:00
void RichTextLabel : : set_visible_characters ( int p_visible ) {
2017-03-05 15:44:50 +00:00
visible_characters = p_visible ;
2015-06-01 22:42:34 +00:00
update ( ) ;
}
int RichTextLabel : : get_visible_characters ( ) const {
return visible_characters ;
}
int RichTextLabel : : get_total_character_count ( ) const {
2017-03-05 15:44:50 +00:00
int tc = 0 ;
for ( int i = 0 ; i < current_frame - > lines . size ( ) ; i + + )
tc + = current_frame - > lines [ i ] . char_count ;
2015-06-01 22:42:34 +00:00
return tc ;
}
2018-07-20 21:14:33 +00:00
void RichTextLabel : : set_fixed_size_to_width ( int p_width ) {
fixed_width = p_width ;
minimum_size_changed ( ) ;
}
Size2 RichTextLabel : : get_minimum_size ( ) const {
if ( fixed_width ! = - 1 ) {
const_cast < RichTextLabel * > ( this ) - > _validate_line_caches ( main ) ;
return Size2 ( fixed_width , const_cast < RichTextLabel * > ( this ) - > get_content_height ( ) ) ;
}
return Size2 ( ) ;
}
2018-11-10 02:44:58 +00:00
Ref < RichTextEffect > RichTextLabel : : _get_custom_effect_by_code ( String p_bbcode_identifier ) {
for ( int i = 0 ; i < custom_effects . size ( ) ; i + + ) {
if ( ! custom_effects [ i ] . is_valid ( ) )
continue ;
if ( custom_effects [ i ] - > get_bbcode ( ) = = p_bbcode_identifier ) {
2019-09-25 20:05:42 +00:00
return custom_effects [ i ] ;
2018-11-10 02:44:58 +00:00
}
}
2019-09-25 20:05:42 +00:00
return Ref < RichTextEffect > ( ) ;
2018-11-10 02:44:58 +00: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 20:51:36 +00:00
# ifdef MODULE_REGEX_ENABLED
2018-11-10 02:44:58 +00: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 ( ) ) {
a . append ( values [ j ] . to_double ( ) ) ;
} else if ( ! numerical . search ( values [ j ] ) . is_null ( ) ) {
a . append ( values [ j ] . to_int ( ) ) ;
} else {
a . append ( values [ j ] ) ;
}
}
2020-02-06 20:51:36 +00:00
# endif
2018-11-10 02:44:58 +00:00
if ( values . size ( ) > 1 ) {
d [ key ] = a ;
} else if ( values . size ( ) = = 1 ) {
d [ key ] = a [ 0 ] ;
}
}
return d ;
}
2014-02-10 01:10:30 +00:00
RichTextLabel : : RichTextLabel ( ) {
2017-03-05 15:44:50 +00:00
main = memnew ( ItemFrame ) ;
main - > index = 0 ;
current = main ;
2015-12-26 13:25:17 +00:00
main - > lines . resize ( 1 ) ;
2018-07-25 01:11:03 +00:00
main - > lines . write [ 0 ] . from = main ;
2017-03-05 15:44:50 +00:00
main - > first_invalid_line = 0 ;
current_frame = main ;
tab_size = 4 ;
default_align = ALIGN_LEFT ;
underline_meta = true ;
2020-04-01 23:20:12 +00:00
meta_hovering = nullptr ;
2017-09-27 17:24:05 +00:00
override_selected_font_color = false ;
2017-03-05 15:44:50 +00:00
scroll_visible = false ;
scroll_follow = false ;
scroll_following = false ;
updating_scroll = false ;
scroll_active = true ;
scroll_w = 0 ;
2018-10-09 00:04:59 +00:00
scroll_updated = false ;
2017-03-05 15:44:50 +00:00
vscroll = memnew ( VScrollBar ) ;
2014-02-10 01:10:30 +00:00
add_child ( vscroll ) ;
2018-09-14 22:55:22 +00:00
vscroll - > set_drag_node ( String ( " .. " ) ) ;
2014-02-10 01:10:30 +00:00
vscroll - > set_step ( 1 ) ;
2017-03-05 15:44:50 +00:00
vscroll - > set_anchor_and_margin ( MARGIN_TOP , ANCHOR_BEGIN , 0 ) ;
vscroll - > set_anchor_and_margin ( MARGIN_BOTTOM , ANCHOR_END , 0 ) ;
vscroll - > set_anchor_and_margin ( MARGIN_RIGHT , ANCHOR_END , 0 ) ;
2020-02-21 17:28:45 +00:00
vscroll - > connect ( " value_changed " , callable_mp ( this , & RichTextLabel : : _scroll_changed ) ) ;
2014-02-10 01:10:30 +00:00
vscroll - > set_step ( 1 ) ;
vscroll - > hide ( ) ;
2017-03-05 15:44:50 +00:00
current_idx = 1 ;
use_bbcode = false ;
2014-02-10 01:10:30 +00:00
2020-04-01 23:20:12 +00:00
selection . click = nullptr ;
2017-03-05 15:44:50 +00:00
selection . active = false ;
selection . enabled = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
visible_characters = - 1 ;
2017-07-03 19:59:58 +00:00
percent_visible = 1 ;
2016-10-17 19:03:47 +00:00
visible_line_count = 0 ;
2015-06-01 22:42:34 +00:00
2018-07-20 21:14:33 +00:00
fixed_width = - 1 ;
2017-01-09 18:50:08 +00:00
set_clip_contents ( true ) ;
2014-02-10 01:10:30 +00:00
}
RichTextLabel : : ~ RichTextLabel ( ) {
2017-03-05 15:44:50 +00:00
memdelete ( main ) ;
2014-02-10 01:10:30 +00:00
}