Browse Source

[RTL] Add options to override underline color and default alpha.

Pāvels Nadtočajevs 4 months ago
parent
commit
332352d37f

+ 10 - 2
doc/classes/RichTextLabel.xml

@@ -526,8 +526,9 @@
 		</method>
 		<method name="push_strikethrough">
 			<return type="void" />
+			<param index="0" name="color" type="Color" default="Color(0, 0, 0, 0)" />
 			<description>
-				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.
 			</description>
 		</method>
 		<method name="push_table">
@@ -542,8 +543,9 @@
 		</method>
 		<method name="push_underline">
 			<return type="void" />
+			<param index="0" name="color" type="Color" default="Color(0, 0, 0, 0)" />
 			<description>
-				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.
 			</description>
 		</method>
 		<method name="reload_effects">
@@ -887,6 +889,9 @@
 		<theme_item name="shadow_outline_size" data_type="constant" type="int" default="1">
 			The size of the shadow outline.
 		</theme_item>
+		<theme_item name="strikethrough_alpha" data_type="constant" type="int" default="50">
+			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).
+		</theme_item>
 		<theme_item name="table_h_separation" data_type="constant" type="int" default="3">
 			The horizontal separation of elements in a table.
 		</theme_item>
@@ -899,6 +904,9 @@
 		<theme_item name="text_highlight_v_padding" data_type="constant" type="int" default="3">
 			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.
 		</theme_item>
+		<theme_item name="underline_alpha" data_type="constant" type="int" default="50">
+			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).
+		</theme_item>
 		<theme_item name="bold_font" data_type="font" type="Font">
 			The font used for bold text.
 		</theme_item>

+ 10 - 1
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.

+ 10 - 0
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

+ 69 - 26
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<ItemUnderline *>(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<ItemStrikethrough *>(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);

+ 11 - 4
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<TextServer::JustificationFlag> 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);

+ 3 - 0
scene/theme/default_theme.cpp

@@ -1190,6 +1190,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &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"]);