diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 24d2d26beb86..fd4fb28c64b2 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -428,8 +428,12 @@
+
+
+
+
- Adds [code skip-lint][ol][/code] or [code skip-lint][ul][/code] tag to the tag stack. Multiplies [param level] by current [member tab_size] to determine new margin length.
+ Adds [code skip-lint][ol][/code] or [code skip-lint][ul][/code] tag to the tag stack. Multiplies [param level] by current [member tab_size] to determine new margin length. If [param break_at_level] is set, list sublevels are reset.
@@ -726,6 +730,9 @@
Each list item has a filled circle marker.
+
+ Each list item has a marker from user defined marker list.
+
Copies the selected text.
diff --git a/misc/extension_api_validation/4.0-stable_4.1-stable.expected b/misc/extension_api_validation/4.0-stable_4.1-stable.expected
index 5c3bf07fb206..f40681fb1e24 100644
--- a/misc/extension_api_validation/4.0-stable_4.1-stable.expected
+++ b/misc/extension_api_validation/4.0-stable_4.1-stable.expected
@@ -231,7 +231,7 @@ Validate extension JSON: Error: Field 'classes/AnimationNode/methods/blend_node/
GH-75017
--------
Validate extension JSON: Error: Hash changed for 'classes/RichTextLabel/methods/push_list', from 8593DF77 to F0951C19. This means that the function has changed and no compatibility function was provided.
-Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/push_list/arguments': size changed value in new API, from 3 to 4.
+Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/push_list/arguments': size changed value in new API, from 3 to 8.
Added an optional parameter with a default value. No adjustments should be necessary.
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index b27f80ee2935..6b357aeac85f 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -364,3 +364,10 @@ GH-92322
Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add_property_editor/arguments': size changed value in new API, from 3 to 4.
Optional arguments added. Compatibility methods registered.
+
+
+GH-92749
+--------
+Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/push_list/arguments': size changed value in new API, from 4 to 8.
+
+Optional arguments added. Compatibility methods registered.
diff --git a/scene/gui/rich_text_label.compat.inc b/scene/gui/rich_text_label.compat.inc
index 97739c4b7900..ccf513f40886 100644
--- a/scene/gui/rich_text_label.compat.inc
+++ b/scene/gui/rich_text_label.compat.inc
@@ -30,6 +30,10 @@
#ifndef DISABLE_DEPRECATED
+void RichTextLabel::_push_list_bind_compat_92749(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet) {
+ push_list(p_level, p_list, p_capitalize, p_bullet, "", ".", -1, false);
+}
+
void RichTextLabel::_push_meta_bind_compat_89024(const Variant &p_meta) {
push_meta(p_meta, RichTextLabel::MetaUnderline::META_UNDERLINE_ALWAYS);
}
@@ -43,6 +47,7 @@ bool RichTextLabel::_remove_paragraph_bind_compat_91098(int p_paragraph) {
}
void RichTextLabel::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("push_list", "level", "type", "capitalize", "bullet"), &RichTextLabel::_push_list_bind_compat_92749, DEFVAL(String::utf8("•")));
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()));
ClassDB::bind_compatibility_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::_remove_paragraph_bind_compat_91098);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 7590cf2ad31f..bb6482cd817a 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -700,9 +700,9 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
break;
}
if (rtl) {
- prefix = prefix + ".";
+ prefix = prefix + list_items[i]->suffix;
} else {
- prefix = "." + prefix;
+ prefix = list_items[i]->suffix + prefix;
}
if (list_items[i]->list_type == LIST_NUMBERS) {
segment = itos(list_index[i]);
@@ -716,11 +716,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (list_items[i]->list_type == LIST_ROMAN) {
segment = _roman(list_index[i], list_items[i]->capitalize);
segments++;
+ } else if (list_items[i]->list_type == LIST_CUSTOM) {
+ Vector letters = list_items[i]->bullet.split(",");
+ segment = letters[(list_index[i] - 1) % letters.size()];
+ if (list_items[i]->capitalize) {
+ segment = segment.to_upper();
+ }
+ segments++;
}
if (rtl) {
- prefix = prefix + segment;
+ prefix = prefix + segment + list_items[i]->prefix;
} else {
- prefix = segment + prefix;
+ prefix = list_items[i]->prefix + segment + prefix;
+ }
+ if (list_items[i]->break_at_level) {
+ break;
}
}
@@ -2381,6 +2391,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref &p_base_font, int
margin += tab_size * font->get_char_size(' ', font_size).width;
} else if (item->type == ITEM_LIST) {
+ int indent_size = static_cast(item)->indent_size;
Ref font = p_base_font;
int font_size = p_base_font_size;
@@ -2397,7 +2408,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref &p_base_font, int
if (font_size_it && font_size_it->font_size > 0) {
font_size = font_size_it->font_size;
}
- margin += tab_size * font->get_char_size(' ', font_size).width;
+ margin += (indent_size >= 0 ? indent_size : tab_size) * font->get_char_size(' ', font_size).width;
}
item = item->parent;
@@ -3556,7 +3567,7 @@ void RichTextLabel::push_indent(int p_level) {
_add_item(item, true, true);
}
-void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet) {
+void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet, const String &p_prefix, const String &p_suffix, int p_indent_size, bool p_break_at_level) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -3570,6 +3581,10 @@ void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize, c
item->level = p_level;
item->capitalize = p_capitalize;
item->bullet = p_bullet;
+ item->break_at_level = p_break_at_level;
+ item->prefix = p_prefix;
+ item->suffix = p_suffix;
+ item->indent_size = p_indent_size;
_add_item(item, true, true);
}
@@ -4366,38 +4381,85 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front(tag);
} else if (tag == "ul") {
indent_level++;
- push_list(indent_level, LIST_DOTS, false);
+ push_list(indent_level, LIST_DOTS, false, U"•", "", "", -1, true);
pos = brk_end + 1;
tag_stack.push_front(tag);
- } else if (tag.begins_with("ul bullet=")) {
- String bullet = tag.substr(10, 1);
+ } else if (tag.begins_with("ul ")) {
+ String bullet = U"•";
+ int indent_size = -1;
+
+ OptionMap::Iterator bullet_option = bbcode_options.find("bullet");
+ if (bullet_option) {
+ bullet = bullet_option->value;
+ }
+ OptionMap::Iterator indent_size_option = bbcode_options.find("indent_size");
+ if (indent_size_option) {
+ indent_size = indent_size_option->value.to_int();
+ }
+
indent_level++;
- push_list(indent_level, LIST_DOTS, false, bullet);
+ push_list(indent_level, LIST_DOTS, false, bullet, "", "", indent_size, true);
pos = brk_end + 1;
tag_stack.push_front("ul");
- } else if ((tag == "ol") || (tag == "ol type=1")) {
+ } else if (tag == "ol") {
indent_level++;
- push_list(indent_level, LIST_NUMBERS, false);
+ push_list(indent_level, LIST_NUMBERS, false, "", "", ".", -1, false);
pos = brk_end + 1;
tag_stack.push_front("ol");
- } else if (tag == "ol type=a") {
- indent_level++;
- push_list(indent_level, LIST_LETTERS, false);
- pos = brk_end + 1;
- tag_stack.push_front("ol");
- } else if (tag == "ol type=A") {
- indent_level++;
- push_list(indent_level, LIST_LETTERS, true);
- pos = brk_end + 1;
- tag_stack.push_front("ol");
- } else if (tag == "ol type=i") {
- indent_level++;
- push_list(indent_level, LIST_ROMAN, false);
- pos = brk_end + 1;
- tag_stack.push_front("ol");
- } else if (tag == "ol type=I") {
+ } else if (tag.begins_with("ol ")) {
+ String custom = "?";
+ String prefix = "";
+ String suffix = ".";
+ ListType list_type = LIST_NUMBERS;
+ bool capitalize = false;
+ bool break_at_level = false;
+ int indent_size = -1;
+
+ OptionMap::Iterator type_option = bbcode_options.find("type");
+ if (type_option) {
+ if (type_option->value == "a") {
+ list_type = LIST_LETTERS;
+ } else if (type_option->value == "A") {
+ list_type = LIST_LETTERS;
+ capitalize = true;
+ } else if (type_option->value == "i") {
+ list_type = LIST_ROMAN;
+ } else if (type_option->value == "I") {
+ list_type = LIST_ROMAN;
+ capitalize = true;
+ } else if (type_option->value == "c") {
+ list_type = LIST_CUSTOM;
+ } else if (type_option->value == "C") {
+ list_type = LIST_CUSTOM;
+ capitalize = true;
+ }
+ }
+ OptionMap::Iterator break_option = bbcode_options.find("break");
+ if (break_option) {
+ if (break_option->value == "y" || break_option->value == "yes" || break_option->value == "t" || break_option->value == "true") {
+ break_at_level = true;
+ } else {
+ break_at_level = false;
+ }
+ }
+ OptionMap::Iterator custom_option = bbcode_options.find("custom");
+ if (custom_option) {
+ custom = custom_option->value;
+ }
+ OptionMap::Iterator prefix_option = bbcode_options.find("prefix");
+ if (prefix_option) {
+ prefix = prefix_option->value;
+ }
+ OptionMap::Iterator suffix_option = bbcode_options.find("suffix");
+ if (suffix_option) {
+ suffix = suffix_option->value;
+ }
+ OptionMap::Iterator indent_size_option = bbcode_options.find("indent_size");
+ if (indent_size_option) {
+ indent_size = indent_size_option->value.to_int();
+ }
indent_level++;
- push_list(indent_level, LIST_ROMAN, true);
+ push_list(indent_level, list_type, capitalize, custom, prefix, suffix, indent_size, break_at_level);
pos = brk_end + 1;
tag_stack.push_front("ol");
} else if (tag == "indent") {
@@ -5820,7 +5882,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color);
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_list", "level", "type", "capitalize", "bullet", "prefix", "suffix", "indent_size", "break_at_level"), &RichTextLabel::push_list, DEFVAL(U"•"), DEFVAL(""), DEFVAL("."), DEFVAL(-1), DEFVAL(false));
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);
@@ -6006,6 +6068,7 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(LIST_LETTERS);
BIND_ENUM_CONSTANT(LIST_ROMAN);
BIND_ENUM_CONSTANT(LIST_DOTS);
+ BIND_ENUM_CONSTANT(LIST_CUSTOM);
BIND_ENUM_CONSTANT(MENU_COPY);
BIND_ENUM_CONSTANT(MENU_SELECT_ALL);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 45dfb88c5598..16d165effb4f 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -57,7 +57,8 @@ class RichTextLabel : public Control {
LIST_NUMBERS,
LIST_LETTERS,
LIST_ROMAN,
- LIST_DOTS
+ LIST_DOTS,
+ LIST_CUSTOM,
};
enum MetaUnderline {
@@ -132,6 +133,7 @@ class RichTextLabel : public Control {
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
+ void _push_list_bind_compat_92749(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = U"•");
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);
bool _remove_paragraph_bind_compat_91098(int p_paragraph);
@@ -320,8 +322,12 @@ class RichTextLabel : public Control {
struct ItemList : public Item {
ListType list_type = LIST_DOTS;
bool capitalize = false;
+ bool break_at_level = false;
int level = 0;
+ int indent_size = -1;
String bullet = U"•";
+ String prefix = U"";
+ String suffix = U".";
ItemList() { type = ITEM_LIST; }
};
@@ -687,7 +693,7 @@ class RichTextLabel : public Control {
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);
- void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = String::utf8("•"));
+ void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = U"•", const String &p_prefix = "", const String &p_suffix = ".", int p_indent_size = -1, bool p_break_at_level = false);
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);