diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 0a2363c116..61e7092249 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -526,8 +526,9 @@ + - Adds a [code skip-lint][s][/code] tag to the tag stack. + Adds a [code skip-lint][s][/code] tag to the tag stack. If [param color] alpha value is zero, current font color with alpha multiplied by [theme_item strikethrough_alpha] is used. @@ -542,8 +543,9 @@ + - Adds a [code skip-lint][u][/code] tag to the tag stack. + Adds a [code skip-lint][u][/code] tag to the tag stack. If [param color] alpha value is zero, current font color with alpha multiplied by [theme_item underline_alpha] is used. @@ -887,6 +889,9 @@ The size of the shadow outline. + + The default strikethrough color transparency (percent). For strikethroughs with a custom color, this theme item is only used if the custom color's alpha is [code]0.0[/code] (fully transparent). + The horizontal separation of elements in a table. @@ -899,6 +904,9 @@ The vertical padding around boxes drawn by the [code][fgcolor][/code] and [code][bgcolor][/code] tags. This does not affect the appearance of text selection. To avoid any risk of neighboring highlights overlapping each other, set this to [code]0[/code] to disable padding. + + The default underline color transparency (percent). For underlines with a custom color, this theme item is only used if the custom color's alpha is [code]0.0[/code] (fully transparent). + The font used for bold text. diff --git a/misc/extension_api_validation/4.4-stable.expected b/misc/extension_api_validation/4.4-stable.expected index 3682f5b554..376e338847 100644 --- a/misc/extension_api_validation/4.4-stable.expected +++ b/misc/extension_api_validation/4.4-stable.expected @@ -100,11 +100,12 @@ Change Node `set_name` to use StringName to improve performance. Compatibility m GH-105570 --------- +--------- Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_create_from_extension/arguments': size changed value in new API, from 9 to 10. Argument added; p_mipmaps. Compatibility method registered. + GH-106121 -------- Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/create_action/arguments': size changed value in new API, from 4 to 5. @@ -258,3 +259,11 @@ GH-106848 Validate extension JSON: API was removed: classes/Node/methods/get_rpc_config Change Node `get_rpc_config` to `get_node_rpc_config`. Compatibility method registered. + + +GH-106300 +--------- +Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RichTextLabel/methods/push_strikethrough': arguments +Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RichTextLabel/methods/push_underline': arguments + +Optional "color" argument added. Compatibility methods registered. diff --git a/scene/gui/rich_text_label.compat.inc b/scene/gui/rich_text_label.compat.inc index 41a944b83c..dfea35efdb 100644 --- a/scene/gui/rich_text_label.compat.inc +++ b/scene/gui/rich_text_label.compat.inc @@ -66,6 +66,14 @@ bool RichTextLabel::_remove_paragraph_bind_compat_91098(int p_paragraph) { return remove_paragraph(p_paragraph, false); } +void RichTextLabel::_push_underline_bind_compat_106300() { + push_underline(Color(0, 0, 0, 0)); +} + +void RichTextLabel::_push_strikethrough_bind_compat_106300() { + push_strikethrough(Color(0, 0, 0, 0)); +} + void RichTextLabel::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::_push_font_bind_compat_79053); ClassDB::bind_compatibility_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::_set_table_column_expand_bind_compat_79053); @@ -76,6 +84,8 @@ void RichTextLabel::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region", "key", "pad", "tooltip", "size_in_percent"), &RichTextLabel::_add_image_bind_compat_76829, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(Variant()), DEFVAL(false), DEFVAL(String()), DEFVAL(false)); ClassDB::bind_compatibility_method(D_METHOD("push_table", "columns", "inline_align", "align_to_row"), &RichTextLabel::_push_table_bind_compat_76829, DEFVAL(INLINE_ALIGNMENT_TOP), DEFVAL(-1)); ClassDB::bind_compatibility_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::_remove_paragraph_bind_compat_91098); + ClassDB::bind_compatibility_method(D_METHOD("push_underline"), &RichTextLabel::_push_underline_bind_compat_106300); + ClassDB::bind_compatibility_method(D_METHOD("push_strikethrough"), &RichTextLabel::_push_strikethrough_bind_compat_106300); } #endif // DISABLE_DEPRECATED diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 5137a277c1..2238227638 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1097,7 +1097,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (step == DRAW_STEP_SHADOW && (font_shadow_color.a == 0)) { continue; } else if (step == DRAW_STEP_TEXT) { - bool has_ul = _find_underline(it); + Color user_ul_color = Color(0, 0, 0, 0); + bool has_ul = _find_underline(it, &user_ul_color); if (!has_ul && underline_meta) { ItemMeta *meta = nullptr; if (_find_meta(it, nullptr, &meta) && meta) { @@ -1115,20 +1116,25 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } if (has_ul) { - if (ul_started && font_color != ul_color_prev) { + Color new_ul_color; + if (user_ul_color.a == 0.0) { + new_ul_color = font_color; + new_ul_color.a *= float(theme_cache.underline_alpha) / 100.0; + } else { + new_ul_color = user_ul_color; + } + if (ul_started && new_ul_color != ul_color_prev) { float y_off = upos; float underline_width = MAX(1.0, uth * theme_cache.base_scale); draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width); ul_start = p_ofs + Vector2(off_step.x, off_step.y); - ul_color_prev = font_color; - ul_color = font_color; - ul_color.a *= 0.5; + ul_color = new_ul_color; + ul_color_prev = new_ul_color; } else if (!ul_started) { ul_started = true; ul_start = p_ofs + Vector2(off_step.x, off_step.y); - ul_color_prev = font_color; - ul_color = font_color; - ul_color.a *= 0.5; + ul_color = new_ul_color; + ul_color_prev = new_ul_color; } } else if (ul_started) { ul_started = false; @@ -1144,13 +1150,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y); dot_ul_color_prev = font_color; dot_ul_color = font_color; - dot_ul_color.a *= 0.5; + dot_ul_color.a *= float(theme_cache.underline_alpha) / 100.0; } else if (!dot_ul_started) { dot_ul_started = true; dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y); dot_ul_color_prev = font_color; dot_ul_color = font_color; - dot_ul_color.a *= 0.5; + dot_ul_color.a *= float(theme_cache.underline_alpha) / 100.0; } } else if (dot_ul_started) { dot_ul_started = false; @@ -1158,21 +1164,27 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o float underline_width = MAX(1.0, uth * theme_cache.base_scale); draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); } - if (_find_strikethrough(it)) { - if (st_started && font_color != st_color_prev) { + Color user_st_color = Color(0, 0, 0, 0); + if (_find_strikethrough(it, &user_st_color)) { + Color new_st_color; + if (user_st_color.a == 0.0) { + new_st_color = font_color; + new_st_color.a *= float(theme_cache.strikethrough_alpha) / 100.0; + } else { + new_st_color = user_st_color; + } + if (st_started && new_st_color != st_color_prev) { float y_off = -l_ascent + l_size.y / 2; float underline_width = MAX(1.0, uth * theme_cache.base_scale); draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width); st_start = p_ofs + Vector2(off_step.x, off_step.y); - st_color_prev = font_color; - st_color = font_color; - st_color.a *= 0.5; + st_color = new_st_color; + st_color_prev = new_st_color; } else if (!st_started) { st_started = true; st_start = p_ofs + Vector2(off_step.x, off_step.y); - st_color_prev = font_color; - st_color = font_color; - st_color.a *= 0.5; + st_color = new_st_color; + st_color_prev = new_st_color; } } else if (st_started) { st_started = false; @@ -3362,11 +3374,15 @@ Color RichTextLabel::_find_outline_color(Item *p_item, const Color &p_default_co return p_default_color; } -bool RichTextLabel::_find_underline(Item *p_item) { +bool RichTextLabel::_find_underline(Item *p_item, Color *r_color) { Item *item = p_item; while (item) { if (item->type == ITEM_UNDERLINE) { + if (r_color) { + ItemUnderline *ul = static_cast(item); + *r_color = ul->color; + } return true; } @@ -3376,11 +3392,15 @@ bool RichTextLabel::_find_underline(Item *p_item) { return false; } -bool RichTextLabel::_find_strikethrough(Item *p_item) { +bool RichTextLabel::_find_strikethrough(Item *p_item, Color *r_color) { Item *item = p_item; while (item) { if (item->type == ITEM_STRIKETHROUGH) { + if (r_color) { + ItemStrikethrough *st = static_cast(item); + *r_color = st->color; + } return true; } @@ -4396,24 +4416,26 @@ void RichTextLabel::push_outline_color(const Color &p_color) { _add_item(item, true); } -void RichTextLabel::push_underline() { +void RichTextLabel::push_underline(const Color &p_color) { _stop_thread(); MutexLock data_lock(data_mutex); ERR_FAIL_COND(current->type == ITEM_TABLE); ItemUnderline *item = memnew(ItemUnderline); + item->color = p_color; item->owner = get_instance_id(); item->rid = items.make_rid(item); _add_item(item, true); } -void RichTextLabel::push_strikethrough() { +void RichTextLabel::push_strikethrough(const Color &p_color) { _stop_thread(); MutexLock data_lock(data_mutex); ERR_FAIL_COND(current->type == ITEM_TABLE); ItemStrikethrough *item = memnew(ItemStrikethrough); + item->color = p_color; item->owner = get_instance_id(); item->rid = items.make_rid(item); @@ -5286,15 +5308,33 @@ void RichTextLabel::append_text(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("cell"); } else if (tag == "u") { - //use underline push_underline(); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag.begins_with("u ")) { + Color color = Color(0, 0, 0, 0); + OptionMap::Iterator color_option = bbcode_options.find("color"); + if (color_option) { + color = Color::from_string(color_option->value, color); + } + + push_underline(color); + pos = brk_end + 1; + tag_stack.push_front("u"); } else if (tag == "s") { - //use strikethrough push_strikethrough(); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag.begins_with("s ")) { + Color color = Color(0, 0, 0, 0); + OptionMap::Iterator color_option = bbcode_options.find("color"); + if (color_option) { + color = Color::from_string(color_option->value, color); + } + + push_strikethrough(color); + pos = brk_end + 1; + tag_stack.push_front("s"); } else if (tag.begins_with("char=")) { int32_t char_code = _get_tag_value(tag).hex_to_int(); add_text(String::chr(char_code)); @@ -7156,8 +7196,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_meta", "data", "underline_mode", "tooltip"), &RichTextLabel::push_meta, DEFVAL(META_UNDERLINE_ALWAYS), DEFVAL(String())); ClassDB::bind_method(D_METHOD("push_hint", "description"), &RichTextLabel::push_hint); ClassDB::bind_method(D_METHOD("push_language", "language"), &RichTextLabel::push_language); - ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); - ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); + ClassDB::bind_method(D_METHOD("push_underline", "color"), &RichTextLabel::push_underline, DEFVAL(Color(0, 0, 0, 0))); + ClassDB::bind_method(D_METHOD("push_strikethrough", "color"), &RichTextLabel::push_strikethrough, DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align", "align_to_row", "name"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP), DEFVAL(-1), DEFVAL(String())); ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio", "shrink"), &RichTextLabel::set_table_column_expand, DEFVAL(1), DEFVAL(true)); @@ -7411,6 +7451,9 @@ void RichTextLabel::_bind_methods() { BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, text_highlight_h_padding); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, text_highlight_v_padding); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, underline_alpha); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, strikethrough_alpha); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, table_h_separation); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, RichTextLabel, table_v_separation); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_odd_row_bg); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 79331c527e..02ae7ade4d 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -141,6 +141,8 @@ protected: void _push_table_bind_compat_76829(int p_columns, InlineAlignment p_alignment, int p_align_to_row); bool _remove_paragraph_bind_compat_91098(int p_paragraph); void _set_table_column_expand_bind_compat_101482(int p_column, bool p_expand, int p_ratio); + void _push_underline_bind_compat_106300(); + void _push_strikethrough_bind_compat_106300(); static void _bind_compatibility_methods(); #endif @@ -323,10 +325,12 @@ private: }; struct ItemUnderline : public Item { + Color color; ItemUnderline() { type = ITEM_UNDERLINE; } }; struct ItemStrikethrough : public Item { + Color color; ItemStrikethrough() { type = ITEM_STRIKETHROUGH; } }; @@ -649,8 +653,8 @@ private: String _find_language(Item *p_item); Color _find_color(Item *p_item, const Color &p_default_color); Color _find_outline_color(Item *p_item, const Color &p_default_color); - bool _find_underline(Item *p_item); - bool _find_strikethrough(Item *p_item); + bool _find_underline(Item *p_item, Color *r_color = nullptr); + bool _find_strikethrough(Item *p_item, Color *r_color = nullptr); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); bool _find_hint(Item *p_item, String *r_description); Color _find_bgcolor(Item *p_item); @@ -740,6 +744,9 @@ private: int text_highlight_h_padding; int text_highlight_v_padding; + int underline_alpha; + int strikethrough_alpha; + int table_h_separation; int table_v_separation; Color table_odd_row_bg; @@ -773,8 +780,8 @@ public: void push_mono(); void push_color(const Color &p_color); void push_outline_color(const Color &p_color); - void push_underline(); - void push_strikethrough(); + void push_underline(const Color &p_color = Color(0, 0, 0, 0)); + void push_strikethrough(const Color &p_color = Color(0, 0, 0, 0)); void push_language(const String &p_language); void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT, BitField p_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE, const PackedFloat32Array &p_tab_stops = PackedFloat32Array()); void push_indent(int p_level); diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp index 3e99b3ae83..fc8d7a21e3 100644 --- a/scene/theme/default_theme.cpp +++ b/scene/theme/default_theme.cpp @@ -1190,6 +1190,9 @@ void fill_default_theme(Ref &theme, const Ref &default_font, const theme->set_constant("text_highlight_h_padding", "RichTextLabel", Math::round(3 * scale)); theme->set_constant("text_highlight_v_padding", "RichTextLabel", Math::round(3 * scale)); + theme->set_constant("underline_alpha", "RichTextLabel", 50); + theme->set_constant("strikethrough_alpha", "RichTextLabel", 50); + // Containers theme->set_icon("h_grabber", "SplitContainer", icons["hsplitter"]);