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"]);