2023-01-05 12:25:55 +00:00
/**************************************************************************/
/* label.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "label.h"
2020-05-12 15:01:17 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
# include "core/string/print_string.h"
# include "core/string/translation.h"
2023-09-08 19:00:10 +00:00
# include "scene/theme/theme_db.h"
2020-09-09 13:00:32 +00:00
# include "servers/text_server.h"
2022-06-15 08:01:45 +00:00
void Label : : set_autowrap_mode ( TextServer : : AutowrapMode p_mode ) {
2022-03-16 07:50:48 +00:00
if ( autowrap_mode = = p_mode ) {
return ;
2020-01-10 06:40:05 +00:00
}
2022-03-16 07:50:48 +00:00
autowrap_mode = p_mode ;
lines_dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-01-10 06:40:05 +00:00
2022-06-15 08:01:45 +00:00
if ( clip | | overrun_behavior ! = TextServer : : OVERRUN_NO_TRIMMING ) {
2021-12-06 13:02:34 +00:00
update_minimum_size ( ) ;
2020-01-10 06:40:05 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2022-06-15 08:01:45 +00:00
TextServer : : AutowrapMode Label : : get_autowrap_mode ( ) const {
2021-07-04 14:43:55 +00:00
return autowrap_mode ;
2014-02-10 01:10:30 +00:00
}
2023-03-23 09:22:37 +00:00
void Label : : set_justification_flags ( BitField < TextServer : : JustificationFlag > p_flags ) {
if ( jst_flags = = p_flags ) {
return ;
}
jst_flags = p_flags ;
lines_dirty = true ;
queue_redraw ( ) ;
}
BitField < TextServer : : JustificationFlag > Label : : get_justification_flags ( ) const {
return jst_flags ;
}
2014-02-10 01:10:30 +00:00
void Label : : set_uppercase ( bool p_uppercase ) {
2022-03-16 07:50:48 +00:00
if ( uppercase = = p_uppercase ) {
return ;
}
2017-03-05 15:44:50 +00:00
uppercase = p_uppercase ;
2020-09-09 13:00:32 +00:00
dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
bool Label : : is_uppercase ( ) const {
return uppercase ;
}
2020-09-09 13:00:32 +00:00
int Label : : get_line_height ( int p_line ) const {
2022-08-31 12:02:40 +00:00
Ref < Font > font = ( settings . is_valid ( ) & & settings - > get_font ( ) . is_valid ( ) ) ? settings - > get_font ( ) : theme_cache . font ;
2020-09-09 13:00:32 +00:00
if ( p_line > = 0 & & p_line < lines_rid . size ( ) ) {
2022-05-09 09:47:10 +00:00
return TS - > shaped_text_get_size ( lines_rid [ p_line ] ) . y ;
2020-09-09 13:00:32 +00:00
} else if ( lines_rid . size ( ) > 0 ) {
int h = 0 ;
for ( int i = 0 ; i < lines_rid . size ( ) ; i + + ) {
2022-05-09 09:47:10 +00:00
h = MAX ( h , TS - > shaped_text_get_size ( lines_rid [ i ] ) . y ) ;
2020-09-09 13:00:32 +00:00
}
return h ;
} else {
2022-08-31 12:02:40 +00:00
int font_size = settings . is_valid ( ) ? settings - > get_font_size ( ) : theme_cache . font_size ;
2022-06-17 08:05:18 +00:00
return font - > get_height ( font_size ) ;
2020-09-09 13:00:32 +00:00
}
}
2023-02-23 09:35:11 +00:00
void Label : : _shape ( ) {
2022-08-31 12:02:40 +00:00
Ref < StyleBox > style = theme_cache . normal_style ;
2020-09-09 13:00:32 +00:00
int width = ( get_size ( ) . width - style - > get_minimum_size ( ) . width ) ;
2022-01-20 07:30:42 +00:00
if ( dirty | | font_dirty ) {
if ( dirty ) {
TS - > shaped_text_clear ( text_rid ) ;
}
2020-09-09 13:00:32 +00:00
if ( text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
TS - > shaped_text_set_direction ( text_rid , is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ) ;
} else {
TS - > shaped_text_set_direction ( text_rid , ( TextServer : : Direction ) text_direction ) ;
}
2022-08-31 12:02:40 +00:00
const Ref < Font > & font = ( settings . is_valid ( ) & & settings - > get_font ( ) . is_valid ( ) ) ? settings - > get_font ( ) : theme_cache . font ;
int font_size = settings . is_valid ( ) ? settings - > get_font_size ( ) : theme_cache . font_size ;
2023-02-23 09:35:11 +00:00
ERR_FAIL_COND ( font . is_null ( ) ) ;
2022-09-29 09:53:28 +00:00
String txt = ( uppercase ) ? TS - > string_to_upper ( xl_text , language ) : xl_text ;
2022-06-15 08:01:45 +00:00
if ( visible_chars > = 0 & & visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING ) {
2022-09-29 09:53:28 +00:00
txt = txt . substr ( 0 , visible_chars ) ;
2021-09-20 06:35:24 +00:00
}
2022-01-20 07:30:42 +00:00
if ( dirty ) {
2022-09-29 09:53:28 +00:00
TS - > shaped_text_add_string ( text_rid , txt , font - > get_rids ( ) , font_size , font - > get_opentype_features ( ) , language ) ;
2022-01-20 07:30:42 +00:00
} else {
int spans = TS - > shaped_get_span_count ( text_rid ) ;
for ( int i = 0 ; i < spans ; i + + ) {
2022-05-09 09:47:10 +00:00
TS - > shaped_set_span_update_font ( text_rid , i , font - > get_rids ( ) , font_size , font - > get_opentype_features ( ) ) ;
2022-01-20 07:30:42 +00:00
}
}
2022-10-08 15:04:02 +00:00
TS - > shaped_text_set_bidi_override ( text_rid , structured_text_parser ( st_parser , st_args , txt ) ) ;
2023-04-16 11:48:43 +00:00
if ( ! tab_stops . is_empty ( ) ) {
TS - > shaped_text_tab_align ( text_rid , tab_stops ) ;
}
2020-09-09 13:00:32 +00:00
dirty = false ;
2022-01-20 07:30:42 +00:00
font_dirty = false ;
2020-09-09 13:00:32 +00:00
lines_dirty = true ;
}
2021-07-04 14:43:55 +00:00
2020-09-09 13:00:32 +00:00
if ( lines_dirty ) {
for ( int i = 0 ; i < lines_rid . size ( ) ; i + + ) {
2022-02-13 12:41:29 +00:00
TS - > free_rid ( lines_rid [ i ] ) ;
2020-09-09 13:00:32 +00:00
}
lines_rid . clear ( ) ;
2023-02-06 13:12:29 +00:00
2023-02-23 09:35:11 +00:00
BitField < TextServer : : LineBreakFlag > autowrap_flags = TextServer : : BREAK_MANDATORY ;
switch ( autowrap_mode ) {
case TextServer : : AUTOWRAP_WORD_SMART :
autowrap_flags = TextServer : : BREAK_WORD_BOUND | TextServer : : BREAK_ADAPTIVE | TextServer : : BREAK_MANDATORY ;
break ;
case TextServer : : AUTOWRAP_WORD :
autowrap_flags = TextServer : : BREAK_WORD_BOUND | TextServer : : BREAK_MANDATORY ;
break ;
case TextServer : : AUTOWRAP_ARBITRARY :
autowrap_flags = TextServer : : BREAK_GRAPHEME_BOUND | TextServer : : BREAK_MANDATORY ;
break ;
case TextServer : : AUTOWRAP_OFF :
break ;
}
autowrap_flags = autowrap_flags | TextServer : : BREAK_TRIM_EDGE_SPACES ;
2023-02-06 13:12:29 +00:00
2023-02-23 09:35:11 +00:00
PackedInt32Array line_breaks = TS - > shaped_text_get_line_breaks ( text_rid , width , 0 , autowrap_flags ) ;
for ( int i = 0 ; i < line_breaks . size ( ) ; i = i + 2 ) {
RID line = TS - > shaped_text_substr ( text_rid , line_breaks [ i ] , line_breaks [ i + 1 ] - line_breaks [ i ] ) ;
2023-04-16 11:48:43 +00:00
if ( ! tab_stops . is_empty ( ) ) {
TS - > shaped_text_tab_align ( line , tab_stops ) ;
}
2023-02-23 09:35:11 +00:00
lines_rid . push_back ( line ) ;
}
}
2021-07-04 14:43:55 +00:00
2023-02-23 09:35:11 +00:00
if ( xl_text . length ( ) = = 0 ) {
minsize = Size2 ( 1 , get_line_height ( ) ) ;
return ;
}
2020-09-09 13:00:32 +00:00
2023-02-23 09:35:11 +00:00
if ( autowrap_mode = = TextServer : : AUTOWRAP_OFF ) {
minsize . width = 0.0f ;
for ( int i = 0 ; i < lines_rid . size ( ) ; i + + ) {
if ( minsize . width < TS - > shaped_text_get_size ( lines_rid [ i ] ) . x ) {
minsize . width = TS - > shaped_text_get_size ( lines_rid [ i ] ) . x ;
2020-09-09 13:00:32 +00:00
}
2023-02-23 09:35:11 +00:00
}
}
2023-02-13 17:46:56 +00:00
2023-02-23 09:35:11 +00:00
if ( lines_dirty ) {
BitField < TextServer : : TextOverrunFlag > overrun_flags = TextServer : : OVERRUN_NO_TRIM ;
switch ( overrun_behavior ) {
case TextServer : : OVERRUN_TRIM_WORD_ELLIPSIS :
overrun_flags . set_flag ( TextServer : : OVERRUN_TRIM ) ;
overrun_flags . set_flag ( TextServer : : OVERRUN_TRIM_WORD_ONLY ) ;
overrun_flags . set_flag ( TextServer : : OVERRUN_ADD_ELLIPSIS ) ;
break ;
case TextServer : : OVERRUN_TRIM_ELLIPSIS :
overrun_flags . set_flag ( TextServer : : OVERRUN_TRIM ) ;
overrun_flags . set_flag ( TextServer : : OVERRUN_ADD_ELLIPSIS ) ;
break ;
case TextServer : : OVERRUN_TRIM_WORD :
overrun_flags . set_flag ( TextServer : : OVERRUN_TRIM ) ;
overrun_flags . set_flag ( TextServer : : OVERRUN_TRIM_WORD_ONLY ) ;
break ;
case TextServer : : OVERRUN_TRIM_CHAR :
overrun_flags . set_flag ( TextServer : : OVERRUN_TRIM ) ;
break ;
case TextServer : : OVERRUN_NO_TRIMMING :
break ;
}
2021-08-10 22:09:48 +00:00
2023-02-23 09:35:11 +00:00
// Fill after min_size calculation.
2021-08-10 22:09:48 +00:00
2023-04-16 11:48:43 +00:00
BitField < TextServer : : JustificationFlag > line_jst_flags = jst_flags ;
if ( ! tab_stops . is_empty ( ) ) {
line_jst_flags . set_flag ( TextServer : : JUSTIFICATION_AFTER_LAST_TAB ) ;
}
2023-02-23 09:35:11 +00:00
if ( autowrap_mode ! = TextServer : : AUTOWRAP_OFF ) {
int visible_lines = get_visible_line_count ( ) ;
bool lines_hidden = visible_lines > 0 & & visible_lines < lines_rid . size ( ) ;
if ( lines_hidden ) {
overrun_flags . set_flag ( TextServer : : OVERRUN_ENFORCE_ELLIPSIS ) ;
}
if ( horizontal_alignment = = HORIZONTAL_ALIGNMENT_FILL ) {
2023-03-23 09:22:37 +00:00
int jst_to_line = visible_lines ;
2023-04-16 11:48:43 +00:00
if ( lines_rid . size ( ) = = 1 & & line_jst_flags . has_flag ( TextServer : : JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE ) ) {
2023-03-23 09:22:37 +00:00
jst_to_line = lines_rid . size ( ) ;
} else {
2023-04-16 11:48:43 +00:00
if ( line_jst_flags . has_flag ( TextServer : : JUSTIFICATION_SKIP_LAST_LINE ) ) {
2023-03-23 09:22:37 +00:00
jst_to_line = visible_lines - 1 ;
}
2023-04-16 11:48:43 +00:00
if ( line_jst_flags . has_flag ( TextServer : : JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS ) ) {
2023-03-23 09:22:37 +00:00
for ( int i = visible_lines - 1 ; i > = 0 ; i - - ) {
if ( TS - > shaped_text_has_visible_chars ( lines_rid [ i ] ) ) {
jst_to_line = i ;
break ;
}
}
}
}
2023-02-23 09:35:11 +00:00
for ( int i = 0 ; i < lines_rid . size ( ) ; i + + ) {
2023-03-23 09:22:37 +00:00
if ( i < jst_to_line ) {
2023-04-16 11:48:43 +00:00
TS - > shaped_text_fit_to_width ( lines_rid [ i ] , width , line_jst_flags ) ;
2023-02-23 09:35:11 +00:00
} else if ( i = = ( visible_lines - 1 ) ) {
2023-03-23 09:22:37 +00:00
TS - > shaped_text_overrun_trim_to_width ( lines_rid [ i ] , width , overrun_flags ) ;
2021-08-10 22:09:48 +00:00
}
2021-07-04 14:43:55 +00:00
}
2023-02-23 09:35:11 +00:00
} else if ( lines_hidden ) {
TS - > shaped_text_overrun_trim_to_width ( lines_rid [ visible_lines - 1 ] , width , overrun_flags ) ;
2023-02-15 07:57:22 +00:00
}
} else {
2023-02-23 09:35:11 +00:00
// Autowrap disabled.
2023-03-23 09:22:37 +00:00
int jst_to_line = lines_rid . size ( ) ;
2023-04-16 11:48:43 +00:00
if ( lines_rid . size ( ) = = 1 & & line_jst_flags . has_flag ( TextServer : : JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE ) ) {
2023-03-23 09:22:37 +00:00
jst_to_line = lines_rid . size ( ) ;
} else {
2023-04-16 11:48:43 +00:00
if ( line_jst_flags . has_flag ( TextServer : : JUSTIFICATION_SKIP_LAST_LINE ) ) {
2023-03-23 09:22:37 +00:00
jst_to_line = lines_rid . size ( ) - 1 ;
}
2023-04-16 11:48:43 +00:00
if ( line_jst_flags . has_flag ( TextServer : : JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS ) ) {
2023-03-23 09:22:37 +00:00
for ( int i = lines_rid . size ( ) - 1 ; i > = 0 ; i - - ) {
if ( TS - > shaped_text_has_visible_chars ( lines_rid [ i ] ) ) {
jst_to_line = i ;
break ;
}
}
}
}
2023-02-23 09:35:11 +00:00
for ( int i = 0 ; i < lines_rid . size ( ) ; i + + ) {
2023-03-23 09:22:37 +00:00
if ( i < jst_to_line & & horizontal_alignment = = HORIZONTAL_ALIGNMENT_FILL ) {
2023-04-16 11:48:43 +00:00
TS - > shaped_text_fit_to_width ( lines_rid [ i ] , width , line_jst_flags ) ;
2023-02-23 09:35:11 +00:00
overrun_flags . set_flag ( TextServer : : OVERRUN_JUSTIFICATION_AWARE ) ;
TS - > shaped_text_overrun_trim_to_width ( lines_rid [ i ] , width , overrun_flags ) ;
2023-04-16 11:48:43 +00:00
TS - > shaped_text_fit_to_width ( lines_rid [ i ] , width , line_jst_flags | TextServer : : JUSTIFICATION_CONSTRAIN_ELLIPSIS ) ;
2023-02-23 09:35:11 +00:00
} else {
TS - > shaped_text_overrun_trim_to_width ( lines_rid [ i ] , width , overrun_flags ) ;
}
}
2023-02-06 13:12:29 +00:00
}
2023-02-23 09:35:11 +00:00
lines_dirty = false ;
2023-02-15 07:57:22 +00:00
}
2020-09-09 13:00:32 +00:00
2023-02-23 09:35:11 +00:00
_update_visible ( ) ;
2020-09-09 13:00:32 +00:00
2023-02-23 09:35:11 +00:00
if ( autowrap_mode = = TextServer : : AUTOWRAP_OFF | | ! clip | | overrun_behavior = = TextServer : : OVERRUN_NO_TRIMMING ) {
2023-02-06 13:12:29 +00:00
update_minimum_size ( ) ;
2020-09-09 13:00:32 +00:00
}
2023-02-23 09:35:11 +00:00
}
void Label : : _update_visible ( ) {
int line_spacing = settings . is_valid ( ) ? settings - > get_line_spacing ( ) : theme_cache . line_spacing ;
Ref < StyleBox > style = theme_cache . normal_style ;
int lines_visible = lines_rid . size ( ) ;
2020-09-09 13:00:32 +00:00
2023-02-23 09:35:11 +00:00
if ( max_lines_visible > = 0 & & lines_visible > max_lines_visible ) {
lines_visible = max_lines_visible ;
}
minsize . height = 0 ;
int last_line = MIN ( lines_rid . size ( ) , lines_visible + lines_skipped ) ;
for ( int64_t i = lines_skipped ; i < last_line ; i + + ) {
minsize . height + = TS - > shaped_text_get_size ( lines_rid [ i ] ) . y + line_spacing ;
}
2023-07-26 05:15:42 +00:00
if ( minsize . height > 0 ) {
minsize . height - = line_spacing ;
}
2014-02-10 01:10:30 +00:00
}
2021-08-27 21:19:51 +00:00
inline void draw_glyph ( const Glyph & p_gl , const RID & p_canvas , const Color & p_font_color , const Vector2 & p_ofs ) {
2021-08-23 11:13:13 +00:00
if ( p_gl . font_rid ! = RID ( ) ) {
TS - > font_draw_glyph ( p_gl . font_rid , p_canvas , p_gl . font_size , p_ofs + Vector2 ( p_gl . x_off , p_gl . y_off ) , p_gl . index , p_font_color ) ;
} else {
TS - > draw_hex_code_box ( p_canvas , p_gl . font_size , p_ofs + Vector2 ( p_gl . x_off , p_gl . y_off ) , p_gl . index , p_font_color ) ;
}
}
2021-08-27 21:19:51 +00:00
inline void draw_glyph_outline ( const Glyph & p_gl , const RID & p_canvas , const Color & p_font_color , const Color & p_font_shadow_color , const Color & p_font_outline_color , const int & p_shadow_outline_size , const int & p_outline_size , const Vector2 & p_ofs , const Vector2 & shadow_ofs ) {
2021-08-10 22:09:48 +00:00
if ( p_gl . font_rid ! = RID ( ) ) {
if ( p_font_shadow_color . a > 0 ) {
TS - > font_draw_glyph ( p_gl . font_rid , p_canvas , p_gl . font_size , p_ofs + Vector2 ( p_gl . x_off , p_gl . y_off ) + shadow_ofs , p_gl . index , p_font_shadow_color ) ;
2021-11-05 22:28:48 +00:00
}
if ( p_font_shadow_color . a > 0 & & p_shadow_outline_size > 0 ) {
TS - > font_draw_glyph_outline ( p_gl . font_rid , p_canvas , p_gl . font_size , p_shadow_outline_size , p_ofs + Vector2 ( p_gl . x_off , p_gl . y_off ) + shadow_ofs , p_gl . index , p_font_shadow_color ) ;
2021-08-10 22:09:48 +00:00
}
if ( p_font_outline_color . a ! = 0.0 & & p_outline_size > 0 ) {
TS - > font_draw_glyph_outline ( p_gl . font_rid , p_canvas , p_gl . font_size , p_outline_size , p_ofs + Vector2 ( p_gl . x_off , p_gl . y_off ) , p_gl . index , p_font_outline_color ) ;
}
}
}
2022-10-08 13:56:21 +00:00
PackedStringArray Label : : get_configuration_warnings ( ) const {
PackedStringArray warnings = Control : : get_configuration_warnings ( ) ;
// Ensure that the font can render all of the required glyphs.
Ref < Font > font ;
if ( settings . is_valid ( ) ) {
font = settings - > get_font ( ) ;
}
if ( font . is_null ( ) ) {
font = theme_cache . font ;
}
if ( font . is_valid ( ) ) {
if ( dirty | | font_dirty | | lines_dirty ) {
const_cast < Label * > ( this ) - > _shape ( ) ;
}
const Glyph * glyph = TS - > shaped_text_get_glyphs ( text_rid ) ;
int64_t glyph_count = TS - > shaped_text_get_glyph_count ( text_rid ) ;
for ( int64_t i = 0 ; i < glyph_count ; i + + ) {
if ( glyph [ i ] . font_rid = = RID ( ) ) {
warnings . push_back ( RTR ( " The current font does not support rendering one or more characters used in this Label's text. " ) ) ;
break ;
}
}
}
return warnings ;
}
2014-02-10 01:10:30 +00:00
void Label : : _notification ( int p_what ) {
2022-02-15 17:06:48 +00:00
switch ( p_what ) {
case NOTIFICATION_TRANSLATION_CHANGED : {
String new_text = atr ( text ) ;
if ( new_text = = xl_text ) {
return ; // Nothing new.
}
xl_text = new_text ;
2022-08-20 18:06:13 +00:00
if ( visible_ratio < 1 ) {
visible_chars = get_total_character_count ( ) * visible_ratio ;
2022-02-15 17:06:48 +00:00
}
dirty = true ;
2017-06-28 20:00:18 +00:00
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2022-10-08 13:56:21 +00:00
update_configuration_warnings ( ) ;
2022-02-15 17:06:48 +00:00
} break ;
2017-01-09 19:43:44 +00:00
2022-02-15 17:06:48 +00:00
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED : {
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2022-02-15 17:06:48 +00:00
} break ;
2021-09-21 07:35:23 +00:00
2022-02-15 17:06:48 +00:00
case NOTIFICATION_DRAW : {
if ( clip ) {
RenderingServer : : get_singleton ( ) - > canvas_item_set_clip ( get_canvas_item ( ) , true ) ;
}
2014-02-10 01:10:30 +00:00
2023-01-28 02:31:44 +00:00
// When a shaped text is invalidated by an external source, we want to reshape it.
if ( ! TS - > shaped_text_is_ready ( text_rid ) ) {
dirty = true ;
}
for ( const RID & line_rid : lines_rid ) {
if ( ! TS - > shaped_text_is_ready ( line_rid ) ) {
lines_dirty = true ;
break ;
}
}
2022-02-15 17:06:48 +00:00
if ( dirty | | font_dirty | | lines_dirty ) {
2023-02-23 09:35:11 +00:00
_shape ( ) ;
2022-02-15 17:06:48 +00:00
}
2014-02-10 01:10:30 +00:00
2022-02-15 17:06:48 +00:00
RID ci = get_canvas_item ( ) ;
2022-06-17 08:05:18 +00:00
bool has_settings = settings . is_valid ( ) ;
2022-02-15 17:06:48 +00:00
Size2 string_size ;
Size2 size = get_size ( ) ;
2022-08-31 12:02:40 +00:00
Ref < StyleBox > style = theme_cache . normal_style ;
Ref < Font > font = ( has_settings & & settings - > get_font ( ) . is_valid ( ) ) ? settings - > get_font ( ) : theme_cache . font ;
Color font_color = has_settings ? settings - > get_font_color ( ) : theme_cache . font_color ;
Color font_shadow_color = has_settings ? settings - > get_shadow_color ( ) : theme_cache . font_shadow_color ;
Point2 shadow_ofs = has_settings ? settings - > get_shadow_offset ( ) : theme_cache . font_shadow_offset ;
int line_spacing = has_settings ? settings - > get_line_spacing ( ) : theme_cache . line_spacing ;
Color font_outline_color = has_settings ? settings - > get_outline_color ( ) : theme_cache . font_outline_color ;
int outline_size = has_settings ? settings - > get_outline_size ( ) : theme_cache . font_outline_size ;
int shadow_outline_size = has_settings ? settings - > get_shadow_size ( ) : theme_cache . font_shadow_outline_size ;
2022-02-15 17:06:48 +00:00
bool rtl = ( TS - > shaped_text_get_inferred_direction ( text_rid ) = = TextServer : : DIRECTION_RTL ) ;
bool rtl_layout = is_layout_rtl ( ) ;
style - > draw ( ci , Rect2 ( Point2 ( 0 , 0 ) , get_size ( ) ) ) ;
float total_h = 0.0 ;
2023-02-15 07:57:22 +00:00
int lines_visible = 0 ;
2022-02-15 17:06:48 +00:00
// Get number of lines to fit to the height.
for ( int64_t i = lines_skipped ; i < lines_rid . size ( ) ; i + + ) {
2022-05-09 09:47:10 +00:00
total_h + = TS - > shaped_text_get_size ( lines_rid [ i ] ) . y + line_spacing ;
2022-02-15 17:06:48 +00:00
if ( total_h > ( get_size ( ) . height - style - > get_minimum_size ( ) . height + line_spacing ) ) {
break ;
}
2023-02-15 07:57:22 +00:00
lines_visible + + ;
2020-09-09 13:00:32 +00:00
}
2015-09-07 18:56:17 +00:00
2023-02-15 07:57:22 +00:00
if ( max_lines_visible > = 0 & & lines_visible > max_lines_visible ) {
lines_visible = max_lines_visible ;
2022-02-15 17:06:48 +00:00
}
2015-09-07 18:56:17 +00:00
2023-02-15 07:57:22 +00:00
int last_line = MIN ( lines_rid . size ( ) , lines_visible + lines_skipped ) ;
2022-06-15 08:01:45 +00:00
bool trim_chars = ( visible_chars > = 0 ) & & ( visible_chars_behavior = = TextServer : : VC_CHARS_AFTER_SHAPING ) ;
bool trim_glyphs_ltr = ( visible_chars > = 0 ) & & ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_LTR ) | | ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_AUTO ) & & ! rtl_layout ) ) ;
bool trim_glyphs_rtl = ( visible_chars > = 0 ) & & ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_RTL ) | | ( ( visible_chars_behavior = = TextServer : : VC_GLYPHS_AUTO ) & & rtl_layout ) ) ;
2022-02-15 17:06:48 +00:00
// Get real total height.
int total_glyphs = 0 ;
total_h = 0 ;
for ( int64_t i = lines_skipped ; i < last_line ; i + + ) {
2022-05-09 09:47:10 +00:00
total_h + = TS - > shaped_text_get_size ( lines_rid [ i ] ) . y + line_spacing ;
2022-02-15 17:06:48 +00:00
total_glyphs + = TS - > shaped_text_get_glyph_count ( lines_rid [ i ] ) + TS - > shaped_text_get_ellipsis_glyph_count ( lines_rid [ i ] ) ;
}
2022-08-20 18:06:13 +00:00
int visible_glyphs = total_glyphs * visible_ratio ;
2022-02-15 17:06:48 +00:00
int processed_glyphs = 0 ;
total_h + = style - > get_margin ( SIDE_TOP ) + style - > get_margin ( SIDE_BOTTOM ) ;
int vbegin = 0 , vsep = 0 ;
2023-02-15 07:57:22 +00:00
if ( lines_visible > 0 ) {
2022-02-15 17:06:48 +00:00
switch ( vertical_alignment ) {
case VERTICAL_ALIGNMENT_TOP : {
// Nothing.
} break ;
case VERTICAL_ALIGNMENT_CENTER : {
vbegin = ( size . y - ( total_h - line_spacing ) ) / 2 ;
vsep = 0 ;
} break ;
case VERTICAL_ALIGNMENT_BOTTOM : {
vbegin = size . y - ( total_h - line_spacing ) ;
2017-03-05 15:44:50 +00:00
vsep = 0 ;
2014-02-10 01:10:30 +00:00
2022-02-15 17:06:48 +00:00
} break ;
case VERTICAL_ALIGNMENT_FILL : {
vbegin = 0 ;
2023-02-15 07:57:22 +00:00
if ( lines_visible > 1 ) {
vsep = ( size . y - ( total_h - line_spacing ) ) / ( lines_visible - 1 ) ;
2022-02-15 17:06:48 +00:00
} else {
vsep = 0 ;
}
} break ;
}
2014-02-10 01:10:30 +00:00
}
2015-09-07 18:56:17 +00:00
2022-02-15 17:06:48 +00:00
Vector2 ofs ;
ofs . y = style - > get_offset ( ) . y + vbegin ;
for ( int i = lines_skipped ; i < last_line ; i + + ) {
Size2 line_size = TS - > shaped_text_get_size ( lines_rid [ i ] ) ;
ofs . x = 0 ;
2022-05-09 09:47:10 +00:00
ofs . y + = TS - > shaped_text_get_ascent ( lines_rid [ i ] ) ;
2022-02-15 17:06:48 +00:00
switch ( horizontal_alignment ) {
case HORIZONTAL_ALIGNMENT_FILL :
2022-06-15 08:01:45 +00:00
if ( rtl & & autowrap_mode ! = TextServer : : AUTOWRAP_OFF ) {
2022-02-15 17:06:48 +00:00
ofs . x = int ( size . width - style - > get_margin ( SIDE_RIGHT ) - line_size . width ) ;
} else {
ofs . x = style - > get_offset ( ) . x ;
}
break ;
case HORIZONTAL_ALIGNMENT_LEFT : {
if ( rtl_layout ) {
ofs . x = int ( size . width - style - > get_margin ( SIDE_RIGHT ) - line_size . width ) ;
} else {
ofs . x = style - > get_offset ( ) . x ;
}
} break ;
case HORIZONTAL_ALIGNMENT_CENTER : {
ofs . x = int ( size . width - line_size . width ) / 2 ;
} break ;
case HORIZONTAL_ALIGNMENT_RIGHT : {
if ( rtl_layout ) {
ofs . x = style - > get_offset ( ) . x ;
} else {
ofs . x = int ( size . width - style - > get_margin ( SIDE_RIGHT ) - line_size . width ) ;
}
} break ;
}
const Glyph * glyphs = TS - > shaped_text_get_glyphs ( lines_rid [ i ] ) ;
int gl_size = TS - > shaped_text_get_glyph_count ( lines_rid [ i ] ) ;
int ellipsis_pos = TS - > shaped_text_get_ellipsis_pos ( lines_rid [ i ] ) ;
int trim_pos = TS - > shaped_text_get_trim_pos ( lines_rid [ i ] ) ;
const Glyph * ellipsis_glyphs = TS - > shaped_text_get_ellipsis_glyphs ( lines_rid [ i ] ) ;
int ellipsis_gl_size = TS - > shaped_text_get_ellipsis_glyph_count ( lines_rid [ i ] ) ;
// Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
int processed_glyphs_ol = processed_glyphs ;
if ( ( outline_size > 0 & & font_outline_color . a ! = 0 ) | | ( font_shadow_color . a ! = 0 ) ) {
Vector2 offset = ofs ;
// Draw RTL ellipsis string when necessary.
if ( rtl & & ellipsis_pos > = 0 ) {
for ( int gl_idx = ellipsis_gl_size - 1 ; gl_idx > = 0 ; gl_idx - - ) {
for ( int j = 0 ; j < ellipsis_glyphs [ gl_idx ] . repeat ; j + + ) {
bool skip = ( trim_chars & & ellipsis_glyphs [ gl_idx ] . end > visible_chars ) | | ( trim_glyphs_ltr & & ( processed_glyphs_ol > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs_ol < total_glyphs - visible_glyphs ) ) ;
//Draw glyph outlines and shadow.
if ( ! skip ) {
draw_glyph_outline ( ellipsis_glyphs [ gl_idx ] , ci , font_color , font_shadow_color , font_outline_color , shadow_outline_size , outline_size , offset , shadow_ofs ) ;
}
processed_glyphs_ol + + ;
offset . x + = ellipsis_glyphs [ gl_idx ] . advance ;
}
}
2021-09-21 07:35:23 +00:00
}
2015-09-07 18:56:17 +00:00
2022-02-15 17:06:48 +00:00
// Draw main text.
for ( int j = 0 ; j < gl_size ; j + + ) {
// Trim when necessary.
if ( trim_pos > = 0 ) {
if ( rtl ) {
if ( j < trim_pos ) {
continue ;
}
} else {
if ( j > = trim_pos ) {
break ;
}
}
}
for ( int k = 0 ; k < glyphs [ j ] . repeat ; k + + ) {
bool skip = ( trim_chars & & glyphs [ j ] . end > visible_chars ) | | ( trim_glyphs_ltr & & ( processed_glyphs_ol > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs_ol < total_glyphs - visible_glyphs ) ) ;
2021-08-27 21:19:51 +00:00
2022-02-15 17:06:48 +00:00
// Draw glyph outlines and shadow.
if ( ! skip ) {
draw_glyph_outline ( glyphs [ j ] , ci , font_color , font_shadow_color , font_outline_color , shadow_outline_size , outline_size , offset , shadow_ofs ) ;
}
processed_glyphs_ol + + ;
offset . x + = glyphs [ j ] . advance ;
}
}
// Draw LTR ellipsis string when necessary.
if ( ! rtl & & ellipsis_pos > = 0 ) {
for ( int gl_idx = 0 ; gl_idx < ellipsis_gl_size ; gl_idx + + ) {
for ( int j = 0 ; j < ellipsis_glyphs [ gl_idx ] . repeat ; j + + ) {
bool skip = ( trim_chars & & ellipsis_glyphs [ gl_idx ] . end > visible_chars ) | | ( trim_glyphs_ltr & & ( processed_glyphs_ol > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs_ol < total_glyphs - visible_glyphs ) ) ;
//Draw glyph outlines and shadow.
if ( ! skip ) {
draw_glyph_outline ( ellipsis_glyphs [ gl_idx ] , ci , font_color , font_shadow_color , font_outline_color , shadow_outline_size , outline_size , offset , shadow_ofs ) ;
}
processed_glyphs_ol + + ;
offset . x + = ellipsis_glyphs [ gl_idx ] . advance ;
}
}
}
}
2021-08-27 21:19:51 +00:00
2022-02-15 17:06:48 +00:00
// Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps.
2021-08-10 22:09:48 +00:00
2021-08-23 11:13:13 +00:00
// Draw RTL ellipsis string when necessary.
2021-08-27 21:19:51 +00:00
if ( rtl & & ellipsis_pos > = 0 ) {
for ( int gl_idx = ellipsis_gl_size - 1 ; gl_idx > = 0 ; gl_idx - - ) {
for ( int j = 0 ; j < ellipsis_glyphs [ gl_idx ] . repeat ; j + + ) {
2022-02-15 17:06:48 +00:00
bool skip = ( trim_chars & & ellipsis_glyphs [ gl_idx ] . end > visible_chars ) | | ( trim_glyphs_ltr & & ( processed_glyphs > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs < total_glyphs - visible_glyphs ) ) ;
2021-08-23 11:13:13 +00:00
//Draw glyph outlines and shadow.
2021-12-05 12:28:32 +00:00
if ( ! skip ) {
2022-02-15 17:06:48 +00:00
draw_glyph ( ellipsis_glyphs [ gl_idx ] , ci , font_color , ofs ) ;
2021-12-05 12:28:32 +00:00
}
2022-02-15 17:06:48 +00:00
processed_glyphs + + ;
ofs . x + = ellipsis_glyphs [ gl_idx ] . advance ;
2021-08-23 11:13:13 +00:00
}
}
}
// Draw main text.
for ( int j = 0 ; j < gl_size ; j + + ) {
2022-01-10 15:24:03 +00:00
// Trim when necessary.
if ( trim_pos > = 0 ) {
if ( rtl ) {
if ( j < trim_pos ) {
continue ;
}
} else {
if ( j > = trim_pos ) {
break ;
2021-08-23 11:13:13 +00:00
}
}
2022-01-10 15:24:03 +00:00
}
for ( int k = 0 ; k < glyphs [ j ] . repeat ; k + + ) {
2022-02-15 17:06:48 +00:00
bool skip = ( trim_chars & & glyphs [ j ] . end > visible_chars ) | | ( trim_glyphs_ltr & & ( processed_glyphs > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs < total_glyphs - visible_glyphs ) ) ;
2021-08-23 11:13:13 +00:00
// Draw glyph outlines and shadow.
2021-12-05 12:28:32 +00:00
if ( ! skip ) {
2022-02-15 17:06:48 +00:00
draw_glyph ( glyphs [ j ] , ci , font_color , ofs ) ;
2021-12-05 12:28:32 +00:00
}
2022-02-15 17:06:48 +00:00
processed_glyphs + + ;
ofs . x + = glyphs [ j ] . advance ;
2021-08-23 11:13:13 +00:00
}
}
// Draw LTR ellipsis string when necessary.
2021-08-27 21:19:51 +00:00
if ( ! rtl & & ellipsis_pos > = 0 ) {
for ( int gl_idx = 0 ; gl_idx < ellipsis_gl_size ; gl_idx + + ) {
for ( int j = 0 ; j < ellipsis_glyphs [ gl_idx ] . repeat ; j + + ) {
2022-02-15 17:06:48 +00:00
bool skip = ( trim_chars & & ellipsis_glyphs [ gl_idx ] . end > visible_chars ) | | ( trim_glyphs_ltr & & ( processed_glyphs > = visible_glyphs ) ) | | ( trim_glyphs_rtl & & ( processed_glyphs < total_glyphs - visible_glyphs ) ) ;
2021-08-23 11:13:13 +00:00
//Draw glyph outlines and shadow.
2021-12-05 12:28:32 +00:00
if ( ! skip ) {
2022-02-15 17:06:48 +00:00
draw_glyph ( ellipsis_glyphs [ gl_idx ] , ci , font_color , ofs ) ;
2021-12-05 12:28:32 +00:00
}
2022-02-15 17:06:48 +00:00
processed_glyphs + + ;
ofs . x + = ellipsis_glyphs [ gl_idx ] . advance ;
2021-12-05 12:28:32 +00:00
}
2021-08-10 22:09:48 +00:00
}
}
2022-05-09 09:47:10 +00:00
ofs . y + = TS - > shaped_text_get_descent ( lines_rid [ i ] ) + vsep + line_spacing ;
2021-08-10 22:09:48 +00:00
}
2022-02-15 17:06:48 +00:00
} break ;
2020-09-09 13:00:32 +00:00
2022-02-15 17:06:48 +00:00
case NOTIFICATION_THEME_CHANGED : {
font_dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2022-02-15 17:06:48 +00:00
} break ;
2015-09-07 18:56:17 +00:00
2022-02-15 17:06:48 +00:00
case NOTIFICATION_RESIZED : {
2023-02-06 13:12:29 +00:00
lines_dirty = true ;
2022-02-15 17:06:48 +00:00
} break ;
2014-02-10 01:10:30 +00:00
}
}
Size2 Label : : get_minimum_size ( ) const {
2018-09-19 17:05:24 +00:00
// don't want to mutable everything
2022-01-20 07:30:42 +00:00
if ( dirty | | font_dirty | | lines_dirty ) {
2020-09-09 13:00:32 +00:00
const_cast < Label * > ( this ) - > _shape ( ) ;
2019-11-04 09:12:15 +00:00
}
2018-09-19 17:05:24 +00:00
2021-02-13 00:13:49 +00:00
Size2 min_size = minsize ;
2022-08-31 12:02:40 +00:00
const Ref < Font > & font = ( settings . is_valid ( ) & & settings - > get_font ( ) . is_valid ( ) ) ? settings - > get_font ( ) : theme_cache . font ;
int font_size = settings . is_valid ( ) ? settings - > get_font_size ( ) : theme_cache . font_size ;
2022-06-17 08:05:18 +00:00
min_size . height = MAX ( min_size . height , font - > get_height ( font_size ) + font - > get_spacing ( TextServer : : SPACING_TOP ) + font - > get_spacing ( TextServer : : SPACING_BOTTOM ) ) ;
2021-02-13 00:13:49 +00:00
2022-08-31 12:02:40 +00:00
Size2 min_style = theme_cache . normal_style - > get_minimum_size ( ) ;
2022-06-15 08:01:45 +00:00
if ( autowrap_mode ! = TextServer : : AUTOWRAP_OFF ) {
return Size2 ( 1 , ( clip | | overrun_behavior ! = TextServer : : OVERRUN_NO_TRIMMING ) ? 1 : min_size . height ) + min_style ;
2020-05-14 14:41:43 +00:00
} else {
2022-06-15 08:01:45 +00:00
if ( clip | | overrun_behavior ! = TextServer : : OVERRUN_NO_TRIMMING ) {
2021-02-13 00:13:49 +00:00
min_size . width = 1 ;
2020-05-14 14:41:43 +00:00
}
2021-02-13 00:13:49 +00:00
return min_size + min_style ;
2015-09-07 18:56:17 +00:00
}
2014-02-10 01:10:30 +00:00
}
int Label : : get_line_count ( ) const {
2020-05-14 14:41:43 +00:00
if ( ! is_inside_tree ( ) ) {
2014-02-10 01:10:30 +00:00
return 1 ;
2020-05-14 14:41:43 +00:00
}
2022-01-20 07:30:42 +00:00
if ( dirty | | font_dirty | | lines_dirty ) {
2020-09-09 13:00:32 +00:00
const_cast < Label * > ( this ) - > _shape ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-09-09 13:00:32 +00:00
return lines_rid . size ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-07-18 21:31:06 +00:00
int Label : : get_visible_line_count ( ) const {
2022-08-31 12:02:40 +00:00
Ref < StyleBox > style = theme_cache . normal_style ;
int line_spacing = settings . is_valid ( ) ? settings - > get_line_spacing ( ) : theme_cache . line_spacing ;
2023-02-15 07:57:22 +00:00
int lines_visible = 0 ;
2021-02-09 17:24:36 +00:00
float total_h = 0.0 ;
2020-09-09 13:00:32 +00:00
for ( int64_t i = lines_skipped ; i < lines_rid . size ( ) ; i + + ) {
2022-05-09 09:47:10 +00:00
total_h + = TS - > shaped_text_get_size ( lines_rid [ i ] ) . y + line_spacing ;
2020-09-09 13:00:32 +00:00
if ( total_h > ( get_size ( ) . height - style - > get_minimum_size ( ) . height + line_spacing ) ) {
break ;
}
2023-02-15 07:57:22 +00:00
lines_visible + + ;
2020-09-09 13:00:32 +00:00
}
2016-07-18 21:31:06 +00:00
2023-02-15 07:57:22 +00:00
if ( lines_visible > lines_rid . size ( ) ) {
lines_visible = lines_rid . size ( ) ;
2020-05-14 14:41:43 +00:00
}
2016-07-18 21:31:06 +00:00
2023-02-15 07:57:22 +00:00
if ( max_lines_visible > = 0 & & lines_visible > max_lines_visible ) {
lines_visible = max_lines_visible ;
2020-05-14 14:41:43 +00:00
}
2016-07-18 21:31:06 +00:00
2023-02-15 07:57:22 +00:00
return lines_visible ;
2016-07-18 21:31:06 +00:00
}
2021-11-25 02:58:47 +00:00
void Label : : set_horizontal_alignment ( HorizontalAlignment p_alignment ) {
ERR_FAIL_INDEX ( ( int ) p_alignment , 4 ) ;
2022-03-16 07:50:48 +00:00
if ( horizontal_alignment = = p_alignment ) {
return ;
2020-09-09 13:00:32 +00:00
}
2022-03-16 07:50:48 +00:00
if ( horizontal_alignment = = HORIZONTAL_ALIGNMENT_FILL | | p_alignment = = HORIZONTAL_ALIGNMENT_FILL ) {
lines_dirty = true ; // Reshape lines.
}
horizontal_alignment = p_alignment ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-11-25 02:58:47 +00:00
HorizontalAlignment Label : : get_horizontal_alignment ( ) const {
return horizontal_alignment ;
2014-02-10 01:10:30 +00:00
}
2021-11-25 02:58:47 +00:00
void Label : : set_vertical_alignment ( VerticalAlignment p_alignment ) {
ERR_FAIL_INDEX ( ( int ) p_alignment , 4 ) ;
2022-03-16 07:50:48 +00:00
if ( vertical_alignment = = p_alignment ) {
return ;
}
2021-11-25 02:58:47 +00:00
vertical_alignment = p_alignment ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-11-25 02:58:47 +00:00
VerticalAlignment Label : : get_vertical_alignment ( ) const {
return vertical_alignment ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void Label : : set_text ( const String & p_string ) {
2020-05-14 14:41:43 +00:00
if ( text = = p_string ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-03-05 15:44:50 +00:00
text = p_string ;
2021-05-27 17:31:33 +00:00
xl_text = atr ( p_string ) ;
2020-09-09 13:00:32 +00:00
dirty = true ;
2022-08-20 18:06:13 +00:00
if ( visible_ratio < 1 ) {
visible_chars = get_total_character_count ( ) * visible_ratio ;
2020-05-14 14:41:43 +00:00
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-12-06 13:02:34 +00:00
update_minimum_size ( ) ;
2022-10-08 13:56:21 +00:00
update_configuration_warnings ( ) ;
2014-02-10 01:10:30 +00:00
}
2022-06-17 08:05:18 +00:00
void Label : : _invalidate ( ) {
font_dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2022-06-17 08:05:18 +00:00
}
void Label : : set_label_settings ( const Ref < LabelSettings > & p_settings ) {
if ( settings ! = p_settings ) {
if ( settings . is_valid ( ) ) {
2023-07-03 19:29:37 +00:00
settings - > disconnect_changed ( callable_mp ( this , & Label : : _invalidate ) ) ;
2022-06-17 08:05:18 +00:00
}
settings = p_settings ;
if ( settings . is_valid ( ) ) {
2023-07-03 19:29:37 +00:00
settings - > connect_changed ( callable_mp ( this , & Label : : _invalidate ) , CONNECT_REFERENCE_COUNTED ) ;
2022-06-17 08:05:18 +00:00
}
_invalidate ( ) ;
}
}
Ref < LabelSettings > Label : : get_label_settings ( ) const {
return settings ;
}
2020-09-09 13:00:32 +00:00
void Label : : set_text_direction ( Control : : TextDirection p_text_direction ) {
ERR_FAIL_COND ( ( int ) p_text_direction < - 1 | | ( int ) p_text_direction > 3 ) ;
if ( text_direction ! = p_text_direction ) {
text_direction = p_text_direction ;
2022-01-20 07:30:42 +00:00
font_dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-09-09 13:00:32 +00:00
}
}
2022-04-19 10:27:18 +00:00
void Label : : set_structured_text_bidi_override ( TextServer : : StructuredTextParser p_parser ) {
2020-09-09 13:00:32 +00:00
if ( st_parser ! = p_parser ) {
st_parser = p_parser ;
2022-04-19 10:27:18 +00:00
dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-09-09 13:00:32 +00:00
}
}
2022-04-19 10:27:18 +00:00
TextServer : : StructuredTextParser Label : : get_structured_text_bidi_override ( ) const {
2020-09-09 13:00:32 +00:00
return st_parser ;
}
void Label : : set_structured_text_bidi_override_options ( Array p_args ) {
2022-03-16 07:50:48 +00:00
if ( st_args = = p_args ) {
return ;
}
2020-09-09 13:00:32 +00:00
st_args = p_args ;
2022-04-19 10:27:18 +00:00
dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-09-09 13:00:32 +00:00
}
Array Label : : get_structured_text_bidi_override_options ( ) const {
return st_args ;
}
Control : : TextDirection Label : : get_text_direction ( ) const {
return text_direction ;
}
void Label : : set_language ( const String & p_language ) {
if ( language ! = p_language ) {
language = p_language ;
dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2020-09-09 13:00:32 +00:00
}
}
String Label : : get_language ( ) const {
return language ;
}
2014-02-10 01:10:30 +00:00
void Label : : set_clip_text ( bool p_clip ) {
2022-03-16 07:50:48 +00:00
if ( clip = = p_clip ) {
return ;
}
2017-03-05 15:44:50 +00:00
clip = p_clip ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-12-06 13:02:34 +00:00
update_minimum_size ( ) ;
2014-02-10 01:10:30 +00:00
}
bool Label : : is_clipping_text ( ) const {
return clip ;
}
2023-04-16 11:48:43 +00:00
void Label : : set_tab_stops ( const PackedFloat32Array & p_tab_stops ) {
if ( tab_stops ! = p_tab_stops ) {
tab_stops = p_tab_stops ;
dirty = true ;
queue_redraw ( ) ;
}
}
PackedFloat32Array Label : : get_tab_stops ( ) const {
return tab_stops ;
}
2022-06-15 08:01:45 +00:00
void Label : : set_text_overrun_behavior ( TextServer : : OverrunBehavior p_behavior ) {
2022-03-16 07:50:48 +00:00
if ( overrun_behavior = = p_behavior ) {
return ;
2021-07-04 14:43:55 +00:00
}
2022-03-16 07:50:48 +00:00
overrun_behavior = p_behavior ;
lines_dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2022-06-15 08:01:45 +00:00
if ( clip | | overrun_behavior ! = TextServer : : OVERRUN_NO_TRIMMING ) {
2021-12-06 13:02:34 +00:00
update_minimum_size ( ) ;
2021-07-04 14:43:55 +00:00
}
}
2022-06-15 08:01:45 +00:00
TextServer : : OverrunBehavior Label : : get_text_overrun_behavior ( ) const {
2021-07-04 14:43:55 +00:00
return overrun_behavior ;
}
2014-02-10 01:10:30 +00:00
String Label : : get_text ( ) const {
return text ;
}
2014-05-31 21:50:10 +00:00
2014-02-10 01:10:30 +00:00
void Label : : set_visible_characters ( int p_amount ) {
2021-09-20 06:35:24 +00:00
if ( visible_chars ! = p_amount ) {
visible_chars = p_amount ;
if ( get_total_character_count ( ) > 0 ) {
2022-08-20 18:06:13 +00:00
visible_ratio = ( float ) p_amount / ( float ) get_total_character_count ( ) ;
2021-09-20 06:35:24 +00:00
} else {
2022-08-20 18:06:13 +00:00
visible_ratio = 1.0 ;
2021-09-20 06:35:24 +00:00
}
2022-06-15 08:01:45 +00:00
if ( visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING ) {
2021-12-05 12:28:32 +00:00
dirty = true ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-08-10 22:09:48 +00:00
}
2014-02-10 01:10:30 +00:00
}
2015-09-07 21:56:16 +00:00
int Label : : get_visible_characters ( ) const {
return visible_chars ;
}
2022-08-20 18:06:13 +00:00
void Label : : set_visible_ratio ( float p_ratio ) {
if ( visible_ratio ! = p_ratio ) {
2022-09-01 17:22:12 +00:00
if ( p_ratio > = 1.0 ) {
2021-09-20 06:35:24 +00:00
visible_chars = - 1 ;
2022-08-20 18:06:13 +00:00
visible_ratio = 1.0 ;
2022-09-01 17:22:12 +00:00
} else if ( p_ratio < 0.0 ) {
2022-08-20 21:17:55 +00:00
visible_chars = 0 ;
2022-08-20 18:06:13 +00:00
visible_ratio = 0.0 ;
2021-09-20 06:35:24 +00:00
} else {
2022-08-20 18:06:13 +00:00
visible_chars = get_total_character_count ( ) * p_ratio ;
visible_ratio = p_ratio ;
2021-09-20 06:35:24 +00:00
}
2022-08-20 21:17:55 +00:00
2022-06-15 08:01:45 +00:00
if ( visible_chars_behavior = = TextServer : : VC_CHARS_BEFORE_SHAPING ) {
2021-12-05 12:28:32 +00:00
dirty = true ;
}
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2015-09-07 18:56:17 +00:00
}
2014-02-10 01:10:30 +00:00
}
2022-08-20 18:06:13 +00:00
float Label : : get_visible_ratio ( ) const {
return visible_ratio ;
2014-02-10 01:10:30 +00:00
}
2022-06-15 08:01:45 +00:00
TextServer : : VisibleCharactersBehavior Label : : get_visible_characters_behavior ( ) const {
2021-12-05 12:28:32 +00:00
return visible_chars_behavior ;
}
2022-06-15 08:01:45 +00:00
void Label : : set_visible_characters_behavior ( TextServer : : VisibleCharactersBehavior p_behavior ) {
2021-12-05 12:28:32 +00:00
if ( visible_chars_behavior ! = p_behavior ) {
visible_chars_behavior = p_behavior ;
dirty = true ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2021-12-05 12:28:32 +00:00
}
}
2015-09-07 18:56:17 +00:00
void Label : : set_lines_skipped ( int p_lines ) {
2021-02-21 13:03:28 +00:00
ERR_FAIL_COND ( p_lines < 0 ) ;
2022-03-16 07:50:48 +00:00
if ( lines_skipped = = p_lines ) {
return ;
}
2017-03-05 15:44:50 +00:00
lines_skipped = p_lines ;
2023-02-23 09:35:11 +00:00
_update_visible ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2015-09-07 18:56:17 +00:00
}
2017-03-05 15:44:50 +00:00
int Label : : get_lines_skipped ( ) const {
2015-09-07 18:56:17 +00:00
return lines_skipped ;
}
void Label : : set_max_lines_visible ( int p_lines ) {
2022-03-16 07:50:48 +00:00
if ( max_lines_visible = = p_lines ) {
return ;
}
2017-03-05 15:44:50 +00:00
max_lines_visible = p_lines ;
2023-02-23 09:35:11 +00:00
_update_visible ( ) ;
2022-08-13 21:21:24 +00:00
queue_redraw ( ) ;
2015-09-07 18:56:17 +00:00
}
2017-03-05 15:44:50 +00:00
int Label : : get_max_lines_visible ( ) const {
2015-09-07 18:56:17 +00:00
return max_lines_visible ;
}
2014-02-10 01:10:30 +00:00
int Label : : get_total_character_count ( ) const {
2022-01-20 07:30:42 +00:00
if ( dirty | | font_dirty | | lines_dirty ) {
2020-09-09 13:00:32 +00:00
const_cast < Label * > ( this ) - > _shape ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-09-09 13:00:32 +00:00
return xl_text . length ( ) ;
}
2014-02-10 01:10:30 +00:00
void Label : : _bind_methods ( ) {
2021-11-25 02:58:47 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_horizontal_alignment " , " alignment " ) , & Label : : set_horizontal_alignment ) ;
ClassDB : : bind_method ( D_METHOD ( " get_horizontal_alignment " ) , & Label : : get_horizontal_alignment ) ;
ClassDB : : bind_method ( D_METHOD ( " set_vertical_alignment " , " alignment " ) , & Label : : set_vertical_alignment ) ;
ClassDB : : bind_method ( D_METHOD ( " get_vertical_alignment " ) , & Label : : get_vertical_alignment ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text " , " text " ) , & Label : : set_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text " ) , & Label : : get_text ) ;
2022-06-17 08:05:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_label_settings " , " settings " ) , & Label : : set_label_settings ) ;
ClassDB : : bind_method ( D_METHOD ( " get_label_settings " ) , & Label : : get_label_settings ) ;
2020-09-09 13:00:32 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text_direction " , " direction " ) , & Label : : set_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text_direction " ) , & Label : : get_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " set_language " , " language " ) , & Label : : set_language ) ;
ClassDB : : bind_method ( D_METHOD ( " get_language " ) , & Label : : get_language ) ;
2021-07-04 14:43:55 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_autowrap_mode " , " autowrap_mode " ) , & Label : : set_autowrap_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_autowrap_mode " ) , & Label : : get_autowrap_mode ) ;
2023-03-23 09:22:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_justification_flags " , " justification_flags " ) , & Label : : set_justification_flags ) ;
ClassDB : : bind_method ( D_METHOD ( " get_justification_flags " ) , & Label : : get_justification_flags ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_clip_text " , " enable " ) , & Label : : set_clip_text ) ;
ClassDB : : bind_method ( D_METHOD ( " is_clipping_text " ) , & Label : : is_clipping_text ) ;
2023-04-16 11:48:43 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_tab_stops " , " tab_stops " ) , & Label : : set_tab_stops ) ;
ClassDB : : bind_method ( D_METHOD ( " get_tab_stops " ) , & Label : : get_tab_stops ) ;
2021-07-04 14:43:55 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text_overrun_behavior " , " overrun_behavior " ) , & Label : : set_text_overrun_behavior ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text_overrun_behavior " ) , & Label : : get_text_overrun_behavior ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_uppercase " , " enable " ) , & Label : : set_uppercase ) ;
ClassDB : : bind_method ( D_METHOD ( " is_uppercase " ) , & Label : : is_uppercase ) ;
2020-09-09 13:00:32 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_line_height " , " line " ) , & Label : : get_line_height , DEFVAL ( - 1 ) ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_line_count " ) , & Label : : get_line_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_line_count " ) , & Label : : get_visible_line_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_total_character_count " ) , & Label : : get_total_character_count ) ;
ClassDB : : bind_method ( D_METHOD ( " set_visible_characters " , " amount " ) , & Label : : set_visible_characters ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_characters " ) , & Label : : get_visible_characters ) ;
2021-12-05 12:28:32 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_visible_characters_behavior " ) , & Label : : get_visible_characters_behavior ) ;
ClassDB : : bind_method ( D_METHOD ( " set_visible_characters_behavior " , " behavior " ) , & Label : : set_visible_characters_behavior ) ;
2022-08-20 18:06:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_visible_ratio " , " ratio " ) , & Label : : set_visible_ratio ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visible_ratio " ) , & Label : : get_visible_ratio ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_lines_skipped " , " lines_skipped " ) , & Label : : set_lines_skipped ) ;
ClassDB : : bind_method ( D_METHOD ( " get_lines_skipped " ) , & Label : : get_lines_skipped ) ;
2023-02-15 07:57:22 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_max_lines_visible " , " lines_visible " ) , & Label : : set_max_lines_visible ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_max_lines_visible " ) , & Label : : get_max_lines_visible ) ;
2020-09-09 13:00:32 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_structured_text_bidi_override " , " parser " ) , & Label : : set_structured_text_bidi_override ) ;
ClassDB : : bind_method ( D_METHOD ( " get_structured_text_bidi_override " ) , & Label : : get_structured_text_bidi_override ) ;
ClassDB : : bind_method ( D_METHOD ( " set_structured_text_bidi_override_options " , " args " ) , & Label : : set_structured_text_bidi_override_options ) ;
ClassDB : : bind_method ( D_METHOD ( " get_structured_text_bidi_override_options " ) , & Label : : get_structured_text_bidi_override_options ) ;
2017-03-05 15:44:50 +00:00
2023-01-09 14:31:44 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2022-06-17 08:05:18 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " label_settings " , PROPERTY_HINT_RESOURCE_TYPE , " LabelSettings " ) , " set_label_settings " , " get_label_settings " ) ;
2021-11-25 02:58:47 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " horizontal_alignment " , PROPERTY_HINT_ENUM , " Left,Center,Right,Fill " ) , " set_horizontal_alignment " , " get_horizontal_alignment " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " vertical_alignment " , PROPERTY_HINT_ENUM , " Top,Center,Bottom,Fill " ) , " set_vertical_alignment " , " get_vertical_alignment " ) ;
2021-07-04 14:43:55 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " autowrap_mode " , PROPERTY_HINT_ENUM , " Off,Arbitrary,Word,Word (Smart) " ) , " set_autowrap_mode " , " get_autowrap_mode " ) ;
2023-03-23 09:22:37 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " justification_flags " , PROPERTY_HINT_FLAGS , " Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128 " ) , " set_justification_flags " , " get_justification_flags " ) ;
2018-11-08 14:30:02 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " clip_text " ) , " set_clip_text " , " is_clipping_text " ) ;
2021-08-10 22:09:48 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " text_overrun_behavior " , PROPERTY_HINT_ENUM , " Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis " ) , " set_text_overrun_behavior " , " get_text_overrun_behavior " ) ;
2018-11-08 14:30:02 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " uppercase " ) , " set_uppercase " , " is_uppercase " ) ;
2023-04-16 11:48:43 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : PACKED_FLOAT32_ARRAY , " tab_stops " ) , " set_tab_stops " , " get_tab_stops " ) ;
2022-08-27 18:18:08 +00:00
ADD_GROUP ( " Displayed Text " , " " ) ;
2022-02-11 10:29:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " lines_skipped " , PROPERTY_HINT_RANGE , " 0,999,1 " ) , " set_lines_skipped " , " get_lines_skipped " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " max_lines_visible " , PROPERTY_HINT_RANGE , " -1,999,1 " ) , " set_max_lines_visible " , " get_max_lines_visible " ) ;
2022-08-20 18:06:13 +00:00
// Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied.
2021-09-20 06:35:24 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " visible_characters " , PROPERTY_HINT_RANGE , " -1,128000,1 " ) , " set_visible_characters " , " get_visible_characters " ) ;
2021-12-05 12:28:32 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " visible_characters_behavior " , PROPERTY_HINT_ENUM , " Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left) " ) , " set_visible_characters_behavior " , " get_visible_characters_behavior " ) ;
2022-08-20 18:06:13 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " visible_ratio " , PROPERTY_HINT_RANGE , " 0,1,0.001 " ) , " set_visible_ratio " , " get_visible_ratio " ) ;
2022-02-11 10:29:17 +00:00
2022-05-09 09:47:10 +00:00
ADD_GROUP ( " BiDi " , " " ) ;
2022-02-11 10:29:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " text_direction " , PROPERTY_HINT_ENUM , " Auto,Left-to-Right,Right-to-Left,Inherited " ) , " set_text_direction " , " get_text_direction " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " language " , PROPERTY_HINT_LOCALE_ID , " " ) , " set_language " , " get_language " ) ;
2020-09-09 13:00:32 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " structured_text_bidi_override " , PROPERTY_HINT_ENUM , " Default,URI,File,Email,List,None,Custom " ) , " set_structured_text_bidi_override " , " get_structured_text_bidi_override " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " structured_text_bidi_override_options " ) , " set_structured_text_bidi_override_options " , " get_structured_text_bidi_override_options " ) ;
2023-09-08 19:00:10 +00:00
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , Label , normal_style , " normal " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , Label , line_spacing ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT , Label , font ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT_SIZE , Label , font_size ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , Label , font_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , Label , font_shadow_color ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_CONSTANT , Label , font_shadow_offset . x , " shadow_offset_x " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_CONSTANT , Label , font_shadow_offset . y , " shadow_offset_y " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , Label , font_outline_color ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_CONSTANT , Label , font_outline_size , " outline_size " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_CONSTANT , Label , font_shadow_outline_size , " shadow_outline_size " ) ;
2014-02-10 01:10:30 +00:00
}
Label : : Label ( const String & p_text ) {
2020-09-09 13:00:32 +00:00
text_rid = TS - > create_shaped_text ( ) ;
2017-01-08 22:54:19 +00:00
set_mouse_filter ( MOUSE_FILTER_IGNORE ) ;
2014-02-10 01:10:30 +00:00
set_text ( p_text ) ;
2017-07-18 01:47:00 +00:00
set_v_size_flags ( SIZE_SHRINK_CENTER ) ;
2014-02-10 01:10:30 +00:00
}
Label : : ~ Label ( ) {
2020-09-09 13:00:32 +00:00
for ( int i = 0 ; i < lines_rid . size ( ) ; i + + ) {
2022-02-13 12:41:29 +00:00
TS - > free_rid ( lines_rid [ i ] ) ;
2015-09-07 18:56:17 +00:00
}
2020-09-09 13:00:32 +00:00
lines_rid . clear ( ) ;
2022-02-13 12:41:29 +00:00
TS - > free_rid ( text_rid ) ;
2014-02-10 01:10:30 +00:00
}