diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index b294b37b8f3d..6e37e395a2f9 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -428,8 +428,10 @@ + Adds a meta tag to the tag stack. Similar to the BBCode [code skip-lint][url=something]{text}[/url][/code], but supports non-[String] metadata types. + If [member meta_underlined] is [code]true[/code], meta tags display an underline. This behavior can be customized with [param underline_mode]. [b]Note:[/b] Meta tags do nothing by default when clicked. To assign behavior when clicked, connect [signal meta_clicked] to a function that is called when the meta tag is clicked. @@ -723,6 +725,15 @@ Represents the size of the [enum MenuItems] enum. + + Meta tag does not display an underline, even if [member meta_underlined] is [code]true[/code]. + + + If [member meta_underlined] is [code]true[/code], meta tag always display an underline. + + + If [member meta_underlined] is [code]true[/code], meta tag display an underline when the mouse cursor is over it. + If this bit is set, [method update_image] changes image texture. diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index c0a1e58c6205..1c36c47246cf 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -224,3 +224,10 @@ Validate extension JSON: JSON file: Field was added in a way that breaks compati Added a return value for add_bone. Should not affect existing regular use - the return value would just be unused. Compatibility method registered. + + +GH-89024 +-------- +Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/push_meta/arguments': size changed value in new API, from 1 to 2. + +Added optional argument. Compatibility method registered. diff --git a/scene/gui/rich_text_label.compat.inc b/scene/gui/rich_text_label.compat.inc index a23c3b94f896..626278a4052d 100644 --- a/scene/gui/rich_text_label.compat.inc +++ b/scene/gui/rich_text_label.compat.inc @@ -30,11 +30,16 @@ #ifndef DISABLE_DEPRECATED +void RichTextLabel::_push_meta_bind_compat_89024(const Variant &p_meta) { + push_meta(p_meta, RichTextLabel::MetaUnderline::META_UNDERLINE_ALWAYS); +} + void RichTextLabel::_add_image_bind_compat_80410(const Ref &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region) { add_image(p_image, p_width, p_height, p_color, p_alignment, p_region, Variant(), false, String(), false); } void RichTextLabel::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("push_meta", "data"), &RichTextLabel::_push_meta_bind_compat_89024); ClassDB::bind_compatibility_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::_add_image_bind_compat_80410, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2())); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1b88caef158c..4226ecbdb3cb 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1223,8 +1223,25 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o for (int i = 0; i < gl_size; i++) { bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end); Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start); + bool has_ul = _find_underline(it); + if (!has_ul && underline_meta) { + ItemMeta *meta = nullptr; + if (_find_meta(it, nullptr, &meta) && meta) { + switch (meta->underline) { + case META_UNDERLINE_ALWAYS: { + has_ul = true; + } break; + case META_UNDERLINE_NEVER: { + has_ul = false; + } break; + case META_UNDERLINE_ON_HOVER: { + has_ul = (meta == meta_hovering); + } break; + } + } + } Color font_color = _find_color(it, p_base_color); - if (_find_underline(it) || (_find_meta(it, nullptr) && underline_meta)) { + if (has_ul) { if (ul_started && font_color != ul_color_prev) { float y_off = TS->shaped_text_get_underline_position(rid); float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale); @@ -2242,6 +2259,7 @@ void RichTextLabel::gui_input(const Ref &p_event) { Variant meta; ItemMeta *item_meta; + ItemMeta *prev_meta = meta_hovering; if (c_item && !outside && _find_meta(c_item, &meta, &item_meta)) { if (meta_hovering != item_meta) { if (meta_hovering) { @@ -2250,11 +2268,17 @@ void RichTextLabel::gui_input(const Ref &p_event) { meta_hovering = item_meta; current_meta = meta; emit_signal(SNAME("meta_hover_started"), meta); + if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) { + queue_redraw(); + } } } else if (meta_hovering) { meta_hovering = nullptr; emit_signal(SNAME("meta_hover_ended"), current_meta); current_meta = false; + if (prev_meta->underline == META_UNDERLINE_ON_HOVER) { + queue_redraw(); + } } } } @@ -3650,7 +3674,7 @@ void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize, c _add_item(item, true, true); } -void RichTextLabel::push_meta(const Variant &p_meta) { +void RichTextLabel::push_meta(const Variant &p_meta, MetaUnderline p_underline_mode) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -3659,6 +3683,7 @@ void RichTextLabel::push_meta(const Variant &p_meta) { item->owner = get_instance_id(); item->rid = items.make_rid(item); item->meta = p_meta; + item->underline = p_underline_mode; _add_item(item, true); } @@ -4573,14 +4598,14 @@ void RichTextLabel::append_text(const String &p_bbcode) { end = p_bbcode.length(); } String url = p_bbcode.substr(brk_end + 1, end - brk_end - 1).unquote(); - push_meta(url); + push_meta(url, META_UNDERLINE_ALWAYS); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag.begins_with("url=")) { String url = tag.substr(4, tag.length()).unquote(); - push_meta(url); + push_meta(url, META_UNDERLINE_ALWAYS); pos = brk_end + 1; tag_stack.push_front("url"); } else if (tag.begins_with("hint=")) { @@ -5891,7 +5916,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser", "justification_flags", "tab_stops"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT), DEFVAL(TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE), DEFVAL(PackedFloat32Array())); ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent); ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize", "bullet"), &RichTextLabel::push_list, DEFVAL(String::utf8("•"))); - ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); + ClassDB::bind_method(D_METHOD("push_meta", "data", "underline_mode"), &RichTextLabel::push_meta, DEFVAL(META_UNDERLINE_ALWAYS)); 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); @@ -6081,6 +6106,10 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_MAX); + BIND_ENUM_CONSTANT(META_UNDERLINE_NEVER); + BIND_ENUM_CONSTANT(META_UNDERLINE_ALWAYS); + BIND_ENUM_CONSTANT(META_UNDERLINE_ON_HOVER); + BIND_BITFIELD_FLAG(UPDATE_TEXTURE); BIND_BITFIELD_FLAG(UPDATE_SIZE); BIND_BITFIELD_FLAG(UPDATE_COLOR); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 0070027a7e07..f8dd9e663ce2 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -51,6 +51,12 @@ class RichTextLabel : public Control { LIST_DOTS }; + enum MetaUnderline { + META_UNDERLINE_NEVER, + META_UNDERLINE_ALWAYS, + META_UNDERLINE_ON_HOVER, + }; + enum ItemType { ITEM_FRAME, ITEM_TEXT, @@ -117,6 +123,7 @@ class RichTextLabel : public Control { static void _bind_methods(); #ifndef DISABLE_DEPRECATED + void _push_meta_bind_compat_89024(const Variant &p_meta); void _add_image_bind_compat_80410(const Ref &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region); static void _bind_compatibility_methods(); #endif @@ -271,6 +278,7 @@ class RichTextLabel : public Control { struct ItemMeta : public Item { Variant meta; + MetaUnderline underline = META_UNDERLINE_ALWAYS; ItemMeta() { type = ITEM_META; } }; @@ -668,7 +676,7 @@ class RichTextLabel : public Control { 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); void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = String::utf8("•")); - void push_meta(const Variant &p_meta); + void push_meta(const Variant &p_meta, MetaUnderline p_underline_mode = META_UNDERLINE_ALWAYS); void push_hint(const String &p_string); void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP, int p_align_to_row = -1); void push_fade(int p_start_index, int p_length); @@ -824,6 +832,7 @@ class RichTextLabel : public Control { VARIANT_ENUM_CAST(RichTextLabel::ListType); VARIANT_ENUM_CAST(RichTextLabel::MenuItems); +VARIANT_ENUM_CAST(RichTextLabel::MetaUnderline); VARIANT_BITFIELD_CAST(RichTextLabel::ImageUpdateMask); #endif // RICH_TEXT_LABEL_H