summaryrefslogtreecommitdiffstats
path: root/scene/gui/rich_text_label.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/rich_text_label.cpp')
-rw-r--r--scene/gui/rich_text_label.cpp947
1 files changed, 587 insertions, 360 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 1da3668ebe..0c3c90d070 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -333,6 +333,8 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
font_size = font_size_it->font_size;
}
TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
+ } else {
+ TS->shaped_set_span_update_font(t, i, p_base_font->get_rids(), p_base_font_size, p_base_font->get_opentype_features());
}
}
@@ -547,7 +549,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (font_size_it && font_size_it->font_size > 0) {
font_size = font_size_it->font_size;
}
- l.text_buf->add_string("\n", font, font_size);
+ l.text_buf->add_string(String::chr(0x200B), font, font_size);
txt += "\n";
l.char_count++;
remaining_characters--;
@@ -817,6 +819,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
int line_count = 0;
+ // Bottom margin for text clipping.
+ float v_limit = theme_cache.normal_style->get_margin(SIDE_BOTTOM);
Size2 ctrl_size = get_size();
// Draw text.
for (int line = 0; line < l.text_buf->get_line_count(); line++) {
@@ -824,7 +828,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
off.y += theme_cache.line_separation;
}
- if (p_ofs.y + off.y >= ctrl_size.height) {
+ if (p_ofs.y + off.y >= ctrl_size.height - v_limit) {
break;
}
@@ -910,84 +914,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
double uth = TS->shaped_text_get_underline_thickness(rid);
off.y += l_ascent;
- // Draw inlined objects.
- Array objects = TS->shaped_text_get_objects(rid);
- for (int i = 0; i < objects.size(); i++) {
- Item *it = items.get_or_null(objects[i]);
- if (it != nullptr) {
- Vector2i obj_range = TS->shaped_text_get_object_range(rid, objects[i]);
- if (trim_chars && l.char_offset + obj_range.y > visible_characters) {
- continue;
- }
- if (trim_glyphs_ltr || trim_glyphs_rtl) {
- int obj_glyph = r_processed_glyphs + TS->shaped_text_get_object_glyph(rid, objects[i]);
- if ((trim_glyphs_ltr && (obj_glyph >= visible_glyphs)) || (trim_glyphs_rtl && (obj_glyph < total_glyphs - visible_glyphs))) {
- continue;
- }
- }
- Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
- switch (it->type) {
- case ITEM_IMAGE: {
- ItemImage *img = static_cast<ItemImage *>(it);
- if (img->pad) {
- Size2 pad_size = rect.size.min(img->image->get_size());
- Vector2 pad_off = (rect.size - pad_size) / 2;
- img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off + pad_off, pad_size), false, img->color);
- } else {
- img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
- }
- } break;
- case ITEM_TABLE: {
- ItemTable *table = static_cast<ItemTable *>(it);
- Color odd_row_bg = theme_cache.table_odd_row_bg;
- Color even_row_bg = theme_cache.table_even_row_bg;
- Color border = theme_cache.table_border;
- float h_separation = theme_cache.table_h_separation;
- float v_separation = theme_cache.table_v_separation;
-
- int col_count = table->columns.size();
- int row_count = table->rows.size();
-
- int idx = 0;
- for (Item *E : table->subitems) {
- ItemFrame *frame = static_cast<ItemFrame *>(E);
-
- int col = idx % col_count;
- int row = idx / col_count;
-
- if (frame->lines.size() != 0 && row < row_count) {
- Vector2 coff = frame->lines[0].offset;
- if (rtl) {
- coff.x = rect.size.width - table->columns[col].width - coff.x;
- }
- if (row % 2 == 0) {
- Color c = frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg;
- if (c.a > 0.0) {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), c, true);
- }
- } else {
- Color c = frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg;
- if (c.a > 0.0) {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), c, true);
- }
- }
- Color bc = frame->border != Color(0, 0, 0, 0) ? frame->border : border;
- if (bc.a > 0.0) {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), bc, false);
- }
- }
-
- for (int j = 0; j < (int)frame->lines.size(); j++) {
- _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs);
- }
- idx++;
- }
- } break;
- default:
- break;
- }
- }
- }
const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
int gl_size = TS->shaped_text_get_glyph_count(rid);
@@ -1003,6 +929,86 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
int processed_glyphs_step = 0;
for (int step = DRAW_STEP_BACKGROUND; step < DRAW_STEP_MAX; step++) {
+ if (step == DRAW_STEP_TEXT) {
+ // Draw inlined objects.
+ Array objects = TS->shaped_text_get_objects(rid);
+ for (int i = 0; i < objects.size(); i++) {
+ Item *it = items.get_or_null(objects[i]);
+ if (it != nullptr) {
+ Vector2i obj_range = TS->shaped_text_get_object_range(rid, objects[i]);
+ if (trim_chars && l.char_offset + obj_range.y > visible_characters) {
+ continue;
+ }
+ if (trim_glyphs_ltr || trim_glyphs_rtl) {
+ int obj_glyph = r_processed_glyphs + TS->shaped_text_get_object_glyph(rid, objects[i]);
+ if ((trim_glyphs_ltr && (obj_glyph >= visible_glyphs)) || (trim_glyphs_rtl && (obj_glyph < total_glyphs - visible_glyphs))) {
+ continue;
+ }
+ }
+ Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
+ switch (it->type) {
+ case ITEM_IMAGE: {
+ ItemImage *img = static_cast<ItemImage *>(it);
+ if (img->pad) {
+ Size2 pad_size = rect.size.min(img->image->get_size());
+ Vector2 pad_off = (rect.size - pad_size) / 2;
+ img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off + pad_off, pad_size), false, img->color);
+ } else {
+ img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
+ }
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ Color odd_row_bg = theme_cache.table_odd_row_bg;
+ Color even_row_bg = theme_cache.table_even_row_bg;
+ Color border = theme_cache.table_border;
+ float h_separation = theme_cache.table_h_separation;
+ float v_separation = theme_cache.table_v_separation;
+
+ int col_count = table->columns.size();
+ int row_count = table->rows.size();
+
+ int idx = 0;
+ for (Item *E : table->subitems) {
+ ItemFrame *frame = static_cast<ItemFrame *>(E);
+
+ int col = idx % col_count;
+ int row = idx / col_count;
+
+ if (frame->lines.size() != 0 && row < row_count) {
+ Vector2 coff = frame->lines[0].offset;
+ if (rtl) {
+ coff.x = rect.size.width - table->columns[col].width - coff.x;
+ }
+ if (row % 2 == 0) {
+ Color c = frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg;
+ if (c.a > 0.0) {
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), c, true);
+ }
+ } else {
+ Color c = frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg;
+ if (c.a > 0.0) {
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), c, true);
+ }
+ }
+ Color bc = frame->border != Color(0, 0, 0, 0) ? frame->border : border;
+ if (bc.a > 0.0) {
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position - Vector2(h_separation * 0.5, v_separation * 0.5).floor(), Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), bc, false);
+ }
+ }
+
+ for (int j = 0; j < (int)frame->lines.size(); j++) {
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs);
+ }
+ idx++;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+ }
Vector2 off_step = off;
processed_glyphs_step = r_processed_glyphs;
@@ -1448,6 +1454,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
bool line_clicked = false;
float text_rect_begin = 0.0;
int char_pos = -1;
+ bool char_clicked = false;
Line &l = p_frame->lines[p_line];
MutexLock lock(l.text_buf->get_mutex());
@@ -1578,6 +1585,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
+ if (!p_meta) {
+ char_pos = rtl ? TS->shaped_text_get_range(rid).y : TS->shaped_text_get_range(rid).x;
+ }
if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) {
if (p_meta) {
int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x);
@@ -1592,6 +1602,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
obj_rect.position.y += baseline_y;
if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
break;
}
@@ -1602,18 +1613,21 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size);
if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
} else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) {
// Hex code box.
Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index);
if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
}
}
} else {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
char_pos = TS->shaped_text_closest_character_pos(rid, char_pos);
+ char_clicked = true;
}
}
line_clicked = true;
@@ -1621,7 +1635,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
// If table hit was detected, and line hit is in the table bounds use table hit.
- if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) {
+ if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || !char_clicked)) {
if (r_click_frame != nullptr) {
*r_click_frame = table_click_frame;
}
@@ -1792,13 +1806,13 @@ void RichTextLabel::_notification(int p_what) {
case NOTIFICATION_RESIZED: {
_stop_thread();
- main->first_resized_line.store(0); //invalidate ALL
+ main->first_resized_line.store(0); // Invalidate all lines.
queue_redraw();
} break;
case NOTIFICATION_THEME_CHANGED: {
_stop_thread();
- main->first_invalid_font_line.store(0); //invalidate ALL
+ main->first_invalid_font_line.store(0); // Invalidate all lines.
queue_redraw();
} break;
@@ -1808,7 +1822,7 @@ void RichTextLabel::_notification(int p_what) {
set_text(text);
}
- main->first_invalid_line.store(0); //invalidate ALL
+ main->first_invalid_line.store(0); // Invalidate all lines.
queue_redraw();
} break;
@@ -1882,10 +1896,12 @@ void RichTextLabel::_notification(int p_what) {
visible_paragraph_count = 0;
visible_line_count = 0;
+ // Bottom margin for text clipping.
+ float v_limit = theme_cache.normal_style->get_margin(SIDE_BOTTOM);
// New cache draw.
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
int processed_glyphs = 0;
- while (ofs.y < size.height && from_line < to_line) {
+ while (ofs.y < size.height - v_limit && from_line < to_line) {
MutexLock lock(main->lines[from_line].text_buf->get_mutex());
visible_paragraph_count++;
@@ -1897,7 +1913,7 @@ void RichTextLabel::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PROCESS: {
if (is_visible_in_tree()) {
- if (!is_ready()) {
+ if (!is_finished()) {
return;
}
double dt = get_process_delta_time();
@@ -2520,7 +2536,7 @@ PackedFloat32Array RichTextLabel::_find_tab_stops(Item *p_item) {
item = item->parent;
}
- return PackedFloat32Array();
+ return default_tab_stops;
}
HorizontalAlignment RichTextLabel::_find_alignment(Item *p_item) {
@@ -2788,7 +2804,7 @@ int RichTextLabel::get_pending_paragraphs() const {
return lines - to_line;
}
-bool RichTextLabel::is_ready() const {
+bool RichTextLabel::is_finished() const {
const_cast<RichTextLabel *>(this)->_validate_line_caches();
if (updating.load()) {
@@ -3031,7 +3047,7 @@ void RichTextLabel::add_text(const String &p_text) {
int pos = 0;
while (pos < p_text.length()) {
- int end = p_text.find("\n", pos);
+ int end = p_text.find_char('\n', pos);
String line;
bool eol = false;
if (end == -1) {
@@ -3398,6 +3414,21 @@ bool RichTextLabel::remove_paragraph(int p_paragraph, bool p_no_invalidate) {
selection.click_item = nullptr;
selection.active = false;
+ if (is_processing_internal()) {
+ bool process_enabled = false;
+ Item *it = main;
+ while (it) {
+ Vector<ItemFX *> fx_stack;
+ _fetch_item_fx_stack(it, fx_stack);
+ if (fx_stack.size()) {
+ process_enabled = true;
+ break;
+ }
+ it = _get_next_item(it, true);
+ }
+ set_process_internal(process_enabled);
+ }
+
if (p_no_invalidate) {
// Do not invalidate cache, only update vertical offsets of the paragraphs after deleted one and scrollbar.
int to_line = main->first_invalid_line.load() - 1;
@@ -3971,6 +4002,7 @@ void RichTextLabel::pop_all() {
void RichTextLabel::clear() {
_stop_thread();
+ set_process_internal(false);
MutexLock data_lock(data_mutex);
main->_clear_children();
@@ -4080,6 +4112,74 @@ void RichTextLabel::parse_bbcode(const String &p_bbcode) {
append_text(p_bbcode);
}
+String RichTextLabel::_get_tag_value(const String &p_tag) {
+ return p_tag.substr(p_tag.find_char('=') + 1);
+}
+
+int RichTextLabel::_find_unquoted(const String &p_src, char32_t p_chr, int p_from) {
+ if (p_from < 0) {
+ return -1;
+ }
+
+ const int len = p_src.length();
+ if (len == 0) {
+ return -1;
+ }
+
+ const char32_t *src = p_src.get_data();
+ bool in_single_quote = false;
+ bool in_double_quote = false;
+ for (int i = p_from; i < len; i++) {
+ if (in_double_quote) {
+ if (src[i] == '"') {
+ in_double_quote = false;
+ }
+ } else if (in_single_quote) {
+ if (src[i] == '\'') {
+ in_single_quote = false;
+ }
+ } else {
+ if (src[i] == '"') {
+ in_double_quote = true;
+ } else if (src[i] == '\'') {
+ in_single_quote = true;
+ } else if (src[i] == p_chr) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+Vector<String> RichTextLabel::_split_unquoted(const String &p_src, char32_t p_splitter) {
+ Vector<String> ret;
+
+ if (p_src.is_empty()) {
+ return ret;
+ }
+
+ int from = 0;
+ int len = p_src.length();
+
+ while (true) {
+ int end = _find_unquoted(p_src, p_splitter, from);
+ if (end < 0) {
+ end = len;
+ }
+ if (end > from) {
+ ret.push_back(p_src.substr(from, end - from));
+ }
+ if (end == len) {
+ break;
+ }
+
+ from = end + 1;
+ }
+
+ return ret;
+}
+
void RichTextLabel::append_text(const String &p_bbcode) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -4095,10 +4195,8 @@ void RichTextLabel::append_text(const String &p_bbcode) {
bool after_list_open_tag = false;
bool after_list_close_tag = false;
- set_process_internal(false);
-
while (pos <= p_bbcode.length()) {
- int brk_pos = p_bbcode.find("[", pos);
+ int brk_pos = p_bbcode.find_char('[', pos);
if (brk_pos < 0) {
brk_pos = p_bbcode.length();
@@ -4123,7 +4221,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
break; //nothing else to add
}
- int brk_end = p_bbcode.find("]", brk_pos + 1);
+ int brk_end = _find_unquoted(p_bbcode, ']', brk_pos + 1);
if (brk_end == -1) {
//no close, add the rest
@@ -4133,7 +4231,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
- Vector<String> split_tag_block = tag.split(" ", false);
+ Vector<String> split_tag_block = _split_unquoted(tag, ' ');
// Find optional parameters.
String bbcode_name;
@@ -4143,7 +4241,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
bbcode_name = split_tag_block[0];
for (int i = 1; i < split_tag_block.size(); i++) {
const String &expr = split_tag_block[i];
- int value_pos = expr.find("=");
+ int value_pos = expr.find_char('=');
if (value_pos > -1) {
bbcode_options[expr.substr(0, value_pos)] = expr.substr(value_pos + 1).unquote();
}
@@ -4154,7 +4252,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
// Find main parameter.
String bbcode_value;
- int main_value_pos = bbcode_name.find("=");
+ int main_value_pos = bbcode_name.find_char('=');
if (main_value_pos > -1) {
bbcode_value = bbcode_name.substr(main_value_pos + 1);
bbcode_name = bbcode_name.substr(0, main_value_pos);
@@ -4253,10 +4351,10 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("table=")) {
- Vector<String> subtag = tag.substr(6, tag.length()).split(",");
+ Vector<String> subtag = _split_unquoted(_get_tag_value(tag), U',');
_normalize_subtags(subtag);
- int columns = subtag[0].to_int();
+ int columns = (subtag.is_empty()) ? 1 : subtag[0].to_int();
if (columns < 1) {
columns = 1;
}
@@ -4303,7 +4401,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("cell=")) {
- int ratio = tag.substr(5, tag.length()).to_int();
+ int ratio = _get_tag_value(tag).to_int();
if (ratio < 1) {
ratio = 1;
}
@@ -4314,54 +4412,45 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("cell");
} else if (tag.begins_with("cell ")) {
- Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
- _normalize_subtags(subtag);
-
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- _normalize_subtags(subtag_a);
-
- if (subtag_a.size() == 2) {
- if (subtag_a[0] == "expand") {
- int ratio = subtag_a[1].to_int();
- if (ratio < 1) {
- ratio = 1;
- }
- set_table_column_expand(get_current_table_column(), true, ratio);
- }
+ OptionMap::Iterator expand_option = bbcode_options.find("expand");
+ if (expand_option) {
+ int ratio = expand_option->value.to_int();
+ if (ratio < 1) {
+ ratio = 1;
}
+ set_table_column_expand(get_current_table_column(), true, ratio);
}
+
push_cell();
const Color fallback_color = Color(0, 0, 0, 0);
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- _normalize_subtags(subtag_a);
-
- if (subtag_a.size() == 2) {
- if (subtag_a[0] == "border") {
- Color color = Color::from_string(subtag_a[1], fallback_color);
- set_cell_border_color(color);
- } else if (subtag_a[0] == "bg") {
- Vector<String> subtag_b = subtag_a[1].split(",");
- _normalize_subtags(subtag_b);
- if (subtag_b.size() == 2) {
- Color color1 = Color::from_string(subtag_b[0], fallback_color);
- Color color2 = Color::from_string(subtag_b[1], fallback_color);
- set_cell_row_background_color(color1, color2);
- }
- if (subtag_b.size() == 1) {
- Color color1 = Color::from_string(subtag_a[1], fallback_color);
- set_cell_row_background_color(color1, color1);
- }
- } else if (subtag_a[0] == "padding") {
- Vector<String> subtag_b = subtag_a[1].split(",");
- _normalize_subtags(subtag_b);
+ OptionMap::Iterator border_option = bbcode_options.find("border");
+ if (border_option) {
+ Color color = Color::from_string(border_option->value, fallback_color);
+ set_cell_border_color(color);
+ }
+ OptionMap::Iterator bg_option = bbcode_options.find("bg");
+ if (bg_option) {
+ Vector<String> subtag_b = _split_unquoted(bg_option->value, U',');
+ _normalize_subtags(subtag_b);
- if (subtag_b.size() == 4) {
- set_cell_padding(Rect2(subtag_b[0].to_float(), subtag_b[1].to_float(), subtag_b[2].to_float(), subtag_b[3].to_float()));
- }
- }
+ if (subtag_b.size() == 2) {
+ Color color1 = Color::from_string(subtag_b[0], fallback_color);
+ Color color2 = Color::from_string(subtag_b[1], fallback_color);
+ set_cell_row_background_color(color1, color2);
+ }
+ if (subtag_b.size() == 1) {
+ Color color1 = Color::from_string(bg_option->value, fallback_color);
+ set_cell_row_background_color(color1, color1);
+ }
+ }
+ OptionMap::Iterator padding_option = bbcode_options.find("padding");
+ if (padding_option) {
+ Vector<String> subtag_b = _split_unquoted(padding_option->value, U',');
+ _normalize_subtags(subtag_b);
+
+ if (subtag_b.size() == 4) {
+ set_cell_padding(Rect2(subtag_b[0].to_float(), subtag_b[1].to_float(), subtag_b[2].to_float(), subtag_b[3].to_float()));
}
}
@@ -4378,7 +4467,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("char=")) {
- int32_t char_code = tag.substr(5, tag.length()).hex_to_int();
+ int32_t char_code = _get_tag_value(tag).hex_to_int();
add_text(String::chr(char_code));
pos = brk_end + 1;
} else if (tag == "lb") {
@@ -4436,19 +4525,19 @@ void RichTextLabel::append_text(const String &p_bbcode) {
add_text(String::chr(0x00AD));
pos = brk_end + 1;
} else if (tag == "center") {
- push_paragraph(HORIZONTAL_ALIGNMENT_CENTER);
+ push_paragraph(HORIZONTAL_ALIGNMENT_CENTER, text_direction, language, st_parser, default_jst_flags, default_tab_stops);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "fill") {
- push_paragraph(HORIZONTAL_ALIGNMENT_FILL);
+ push_paragraph(HORIZONTAL_ALIGNMENT_FILL, text_direction, language, st_parser, default_jst_flags, default_tab_stops);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "left") {
- push_paragraph(HORIZONTAL_ALIGNMENT_LEFT);
+ push_paragraph(HORIZONTAL_ALIGNMENT_LEFT, text_direction, language, st_parser, default_jst_flags, default_tab_stops);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "right") {
- push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT);
+ push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT, text_direction, language, st_parser, default_jst_flags, default_tab_stops);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "ul") {
@@ -4457,7 +4546,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("ul bullet=")) {
- String bullet = tag.substr(10, 1);
+ String bullet = _get_tag_value(tag);
indent_level++;
push_list(indent_level, LIST_DOTS, false, bullet);
pos = brk_end + 1;
@@ -4493,7 +4582,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("lang=")) {
- String lang = tag.substr(5, tag.length()).unquote();
+ String lang = _get_tag_value(tag).unquote();
push_language(lang);
pos = brk_end + 1;
tag_stack.push_front("lang");
@@ -4502,89 +4591,104 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("p");
} else if (tag.begins_with("p ")) {
- Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
- _normalize_subtags(subtag);
-
HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED;
- String lang;
- PackedFloat32Array tab_stops;
+ String lang = language;
+ PackedFloat32Array tab_stops = default_tab_stops;
TextServer::StructuredTextParser st_parser_type = TextServer::STRUCTURED_TEXT_DEFAULT;
BitField<TextServer::JustificationFlag> jst_flags = default_jst_flags;
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- _normalize_subtags(subtag_a);
-
- if (subtag_a.size() == 2) {
- if (subtag_a[0] == "justification_flags" || subtag_a[0] == "jst") {
- Vector<String> subtag_b = subtag_a[1].split(",");
- jst_flags = 0; // Clear flags.
- for (const String &E : subtag_b) {
- if (E == "kashida" || E == "k") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_KASHIDA);
- } else if (E == "word" || E == "w") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_WORD_BOUND);
- } else if (E == "trim" || E == "tr") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
- } else if (E == "after_last_tab" || E == "lt") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_AFTER_LAST_TAB);
- } else if (E == "skip_last" || E == "sl") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE);
- } else if (E == "skip_last_with_chars" || E == "sv") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS);
- } else if (E == "do_not_skip_single" || E == "ns") {
- jst_flags.set_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE);
- }
- }
- } else if (subtag_a[0] == "tab_stops") {
- Vector<String> splitters;
- splitters.push_back(",");
- splitters.push_back(";");
- tab_stops = subtag_a[1].split_floats_mk(splitters);
- } else if (subtag_a[0] == "align") {
- if (subtag_a[1] == "l" || subtag_a[1] == "left") {
- alignment = HORIZONTAL_ALIGNMENT_LEFT;
- } else if (subtag_a[1] == "c" || subtag_a[1] == "center") {
- alignment = HORIZONTAL_ALIGNMENT_CENTER;
- } else if (subtag_a[1] == "r" || subtag_a[1] == "right") {
- alignment = HORIZONTAL_ALIGNMENT_RIGHT;
- } else if (subtag_a[1] == "f" || subtag_a[1] == "fill") {
- alignment = HORIZONTAL_ALIGNMENT_FILL;
- }
- } else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") {
- if (subtag_a[1] == "a" || subtag_a[1] == "auto") {
- dir = Control::TEXT_DIRECTION_AUTO;
- } else if (subtag_a[1] == "l" || subtag_a[1] == "ltr") {
- dir = Control::TEXT_DIRECTION_LTR;
- } else if (subtag_a[1] == "r" || subtag_a[1] == "rtl") {
- dir = Control::TEXT_DIRECTION_RTL;
- }
- } else if (subtag_a[0] == "lang" || subtag_a[0] == "language") {
- lang = subtag_a[1];
- } else if (subtag_a[0] == "st" || subtag_a[0] == "bidi_override") {
- if (subtag_a[1] == "d" || subtag_a[1] == "default") {
- st_parser_type = TextServer::STRUCTURED_TEXT_DEFAULT;
- } else if (subtag_a[1] == "u" || subtag_a[1] == "uri") {
- st_parser_type = TextServer::STRUCTURED_TEXT_URI;
- } else if (subtag_a[1] == "f" || subtag_a[1] == "file") {
- st_parser_type = TextServer::STRUCTURED_TEXT_FILE;
- } else if (subtag_a[1] == "e" || subtag_a[1] == "email") {
- st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL;
- } else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
- st_parser_type = TextServer::STRUCTURED_TEXT_LIST;
- } else if (subtag_a[1] == "n" || subtag_a[1] == "gdscript") {
- st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT;
- } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
- st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM;
- }
+
+ OptionMap::Iterator justification_flags_option = bbcode_options.find("justification_flags");
+ if (!justification_flags_option) {
+ justification_flags_option = bbcode_options.find("jst");
+ }
+ if (justification_flags_option) {
+ Vector<String> subtag_b = _split_unquoted(justification_flags_option->value, U',');
+ jst_flags = 0; // Clear flags.
+ for (const String &E : subtag_b) {
+ if (E == "kashida" || E == "k") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_KASHIDA);
+ } else if (E == "word" || E == "w") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_WORD_BOUND);
+ } else if (E == "trim" || E == "tr") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
+ } else if (E == "after_last_tab" || E == "lt") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_AFTER_LAST_TAB);
+ } else if (E == "skip_last" || E == "sl") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE);
+ } else if (E == "skip_last_with_chars" || E == "sv") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS);
+ } else if (E == "do_not_skip_single" || E == "ns") {
+ jst_flags.set_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE);
}
}
}
+ OptionMap::Iterator tab_stops_option = bbcode_options.find("tab_stops");
+ if (tab_stops_option) {
+ Vector<String> splitters;
+ splitters.push_back(",");
+ splitters.push_back(";");
+ tab_stops = tab_stops_option->value.split_floats_mk(splitters);
+ }
+ OptionMap::Iterator align_option = bbcode_options.find("align");
+ if (align_option) {
+ if (align_option->value == "l" || align_option->value == "left") {
+ alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ } else if (align_option->value == "c" || align_option->value == "center") {
+ alignment = HORIZONTAL_ALIGNMENT_CENTER;
+ } else if (align_option->value == "r" || align_option->value == "right") {
+ alignment = HORIZONTAL_ALIGNMENT_RIGHT;
+ } else if (align_option->value == "f" || align_option->value == "fill") {
+ alignment = HORIZONTAL_ALIGNMENT_FILL;
+ }
+ }
+ OptionMap::Iterator direction_option = bbcode_options.find("direction");
+ if (!direction_option) {
+ direction_option = bbcode_options.find("dir");
+ }
+ if (direction_option) {
+ if (direction_option->value == "a" || direction_option->value == "auto") {
+ dir = Control::TEXT_DIRECTION_AUTO;
+ } else if (direction_option->value == "l" || direction_option->value == "ltr") {
+ dir = Control::TEXT_DIRECTION_LTR;
+ } else if (direction_option->value == "r" || direction_option->value == "rtl") {
+ dir = Control::TEXT_DIRECTION_RTL;
+ }
+ }
+ OptionMap::Iterator language_option = bbcode_options.find("language");
+ if (!language_option) {
+ language_option = bbcode_options.find("lang");
+ }
+ if (language_option) {
+ lang = language_option->value;
+ }
+ OptionMap::Iterator bidi_override_option = bbcode_options.find("bidi_override");
+ if (!bidi_override_option) {
+ bidi_override_option = bbcode_options.find("st");
+ }
+ if (bidi_override_option) {
+ if (bidi_override_option->value == "d" || bidi_override_option->value == "default") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_DEFAULT;
+ } else if (bidi_override_option->value == "u" || bidi_override_option->value == "uri") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_URI;
+ } else if (bidi_override_option->value == "f" || bidi_override_option->value == "file") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_FILE;
+ } else if (bidi_override_option->value == "e" || bidi_override_option->value == "email") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL;
+ } else if (bidi_override_option->value == "l" || bidi_override_option->value == "list") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_LIST;
+ } else if (bidi_override_option->value == "n" || bidi_override_option->value == "gdscript") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT;
+ } else if (bidi_override_option->value == "c" || bidi_override_option->value == "custom") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM;
+ }
+ }
+
push_paragraph(alignment, dir, lang, st_parser_type, jst_flags, tab_stops);
pos = brk_end + 1;
tag_stack.push_front("p");
} else if (tag == "url") {
- int end = p_bbcode.find("[", brk_end);
+ int end = p_bbcode.find_char('[', brk_end);
if (end == -1) {
end = p_bbcode.length();
}
@@ -4595,19 +4699,16 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front(tag);
} else if (tag.begins_with("url=")) {
- String url = tag.substr(4, tag.length()).unquote();
+ String url = _get_tag_value(tag).unquote();
push_meta(url, META_UNDERLINE_ALWAYS);
pos = brk_end + 1;
tag_stack.push_front("url");
} else if (tag.begins_with("hint=")) {
- String description = tag.substr(5, tag.length()).unquote();
+ String description = _get_tag_value(tag).unquote();
push_hint(description);
pos = brk_end + 1;
tag_stack.push_front("hint");
} else if (tag.begins_with("dropcap")) {
- Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
- _normalize_subtags(subtag);
-
int fs = theme_cache.normal_font_size * 3;
Ref<Font> f = theme_cache.normal_font;
Color color = theme_cache.default_color;
@@ -4615,39 +4716,47 @@ void RichTextLabel::append_text(const String &p_bbcode) {
int outline_size = theme_cache.outline_size;
Rect2 dropcap_margins;
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- _normalize_subtags(subtag_a);
-
- if (subtag_a.size() == 2) {
- if (subtag_a[0] == "font" || subtag_a[0] == "f") {
- const String &fnt = subtag_a[1];
- Ref<Font> font = ResourceLoader::load(fnt, "Font");
- if (font.is_valid()) {
- f = font;
- }
- } else if (subtag_a[0] == "font_size") {
- fs = subtag_a[1].to_int();
- } else if (subtag_a[0] == "margins") {
- Vector<String> subtag_b = subtag_a[1].split(",");
- _normalize_subtags(subtag_b);
+ OptionMap::Iterator font_option = bbcode_options.find("font");
+ if (!font_option) {
+ font_option = bbcode_options.find("f");
+ }
+ if (font_option) {
+ const String &fnt = font_option->value;
+ Ref<Font> font = ResourceLoader::load(fnt, "Font");
+ if (font.is_valid()) {
+ f = font;
+ }
+ }
+ OptionMap::Iterator font_size_option = bbcode_options.find("font_size");
+ if (font_size_option) {
+ fs = font_size_option->value.to_int();
+ }
+ OptionMap::Iterator margins_option = bbcode_options.find("margins");
+ if (margins_option) {
+ Vector<String> subtag_b = _split_unquoted(margins_option->value, U',');
+ _normalize_subtags(subtag_b);
- if (subtag_b.size() == 4) {
- dropcap_margins.position.x = subtag_b[0].to_float();
- dropcap_margins.position.y = subtag_b[1].to_float();
- dropcap_margins.size.x = subtag_b[2].to_float();
- dropcap_margins.size.y = subtag_b[3].to_float();
- }
- } else if (subtag_a[0] == "outline_size") {
- outline_size = subtag_a[1].to_int();
- } else if (subtag_a[0] == "color") {
- color = Color::from_string(subtag_a[1], color);
- } else if (subtag_a[0] == "outline_color") {
- outline_color = Color::from_string(subtag_a[1], outline_color);
- }
+ if (subtag_b.size() == 4) {
+ dropcap_margins.position.x = subtag_b[0].to_float();
+ dropcap_margins.position.y = subtag_b[1].to_float();
+ dropcap_margins.size.x = subtag_b[2].to_float();
+ dropcap_margins.size.y = subtag_b[3].to_float();
}
}
- int end = p_bbcode.find("[", brk_end);
+ OptionMap::Iterator outline_size_option = bbcode_options.find("outline_size");
+ if (outline_size_option) {
+ outline_size = outline_size_option->value.to_int();
+ }
+ OptionMap::Iterator color_option = bbcode_options.find("color");
+ if (color_option) {
+ color = Color::from_string(color_option->value, color);
+ }
+ OptionMap::Iterator outline_color_option = bbcode_options.find("outline_color");
+ if (outline_color_option) {
+ outline_color = Color::from_string(outline_color_option->value, outline_color);
+ }
+
+ int end = p_bbcode.find_char('[', brk_end);
if (end == -1) {
end = p_bbcode.length();
}
@@ -4661,7 +4770,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
} else if (tag.begins_with("img")) {
int alignment = INLINE_ALIGNMENT_CENTER;
if (tag.begins_with("img=")) {
- Vector<String> subtag = tag.substr(4, tag.length()).split(",");
+ Vector<String> subtag = _split_unquoted(_get_tag_value(tag), U',');
_normalize_subtags(subtag);
if (subtag.size() > 1) {
@@ -4692,7 +4801,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
- int end = p_bbcode.find("[", brk_end);
+ int end = p_bbcode.find_char('[', brk_end);
if (end == -1) {
end = p_bbcode.length();
}
@@ -4704,7 +4813,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
Rect2 region;
OptionMap::Iterator region_option = bbcode_options.find("region");
if (region_option) {
- Vector<String> region_values = region_option->value.split(",", false);
+ Vector<String> region_values = _split_unquoted(region_option->value, U',');
if (region_values.size() == 4) {
region.position.x = region_values[0].to_float();
region.position.y = region_values[1].to_float();
@@ -4766,27 +4875,27 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = end;
tag_stack.push_front(bbcode_name);
} else if (tag.begins_with("color=")) {
- String color_str = tag.substr(6, tag.length()).unquote();
+ String color_str = _get_tag_value(tag).unquote();
Color color = Color::from_string(color_str, theme_cache.default_color);
push_color(color);
pos = brk_end + 1;
tag_stack.push_front("color");
} else if (tag.begins_with("outline_color=")) {
- String color_str = tag.substr(14, tag.length()).unquote();
+ String color_str = _get_tag_value(tag).unquote();
Color color = Color::from_string(color_str, theme_cache.default_color);
push_outline_color(color);
pos = brk_end + 1;
tag_stack.push_front("outline_color");
} else if (tag.begins_with("font_size=")) {
- int fnt_size = tag.substr(10, tag.length()).to_int();
+ int fnt_size = _get_tag_value(tag).to_int();
push_font_size(fnt_size);
pos = brk_end + 1;
tag_stack.push_front("font_size");
} else if (tag.begins_with("opentype_features=") || tag.begins_with("otf=")) {
- int value_pos = tag.find("=");
+ int value_pos = tag.find_char('=');
String fnt_ftr = tag.substr(value_pos + 1);
Vector<String> subtag = fnt_ftr.split(",");
_normalize_subtags(subtag);
@@ -4830,7 +4939,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front(tag.substr(0, value_pos));
} else if (tag.begins_with("font=")) {
- String fnt = tag.substr(5, tag.length()).unquote();
+ String fnt = _get_tag_value(tag).unquote();
Ref<Font> fc = ResourceLoader::load(fnt, "Font");
if (fc.is_valid()) {
@@ -4841,11 +4950,9 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front("font");
} else if (tag.begins_with("font ")) {
- Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
- _normalize_subtags(subtag);
-
Ref<Font> font = theme_cache.normal_font;
DefaultFont def_font = NORMAL_FONT;
+ int fnt_size = -1;
ItemFont *font_it = _find_font(current);
if (font_it) {
@@ -4858,75 +4965,122 @@ void RichTextLabel::append_text(const String &p_bbcode) {
Ref<FontVariation> fc;
fc.instantiate();
- int fnt_size = -1;
- for (int i = 1; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=", true, 1);
- _normalize_subtags(subtag_a);
-
- if (subtag_a.size() == 2) {
- if (subtag_a[0] == "name" || subtag_a[0] == "n") {
- const String &fnt = subtag_a[1];
- Ref<Font> font_data = ResourceLoader::load(fnt, "Font");
- if (font_data.is_valid()) {
- font = font_data;
- def_font = CUSTOM_FONT;
- }
- } else if (subtag_a[0] == "size" || subtag_a[0] == "s") {
- fnt_size = subtag_a[1].to_int();
- } else if (subtag_a[0] == "glyph_spacing" || subtag_a[0] == "gl") {
- int spacing = subtag_a[1].to_int();
- fc->set_spacing(TextServer::SPACING_GLYPH, spacing);
- } else if (subtag_a[0] == "space_spacing" || subtag_a[0] == "sp") {
- int spacing = subtag_a[1].to_int();
- fc->set_spacing(TextServer::SPACING_SPACE, spacing);
- } else if (subtag_a[0] == "top_spacing" || subtag_a[0] == "top") {
- int spacing = subtag_a[1].to_int();
- fc->set_spacing(TextServer::SPACING_TOP, spacing);
- } else if (subtag_a[0] == "bottom_spacing" || subtag_a[0] == "bt") {
- int spacing = subtag_a[1].to_int();
- fc->set_spacing(TextServer::SPACING_BOTTOM, spacing);
- } else if (subtag_a[0] == "embolden" || subtag_a[0] == "emb") {
- float emb = subtag_a[1].to_float();
- fc->set_variation_embolden(emb);
- } else if (subtag_a[0] == "face_index" || subtag_a[0] == "fi") {
- int fi = subtag_a[1].to_int();
- fc->set_variation_face_index(fi);
- } else if (subtag_a[0] == "slant" || subtag_a[0] == "sln") {
- float slant = subtag_a[1].to_float();
- fc->set_variation_transform(Transform2D(1.0, slant, 0.0, 1.0, 0.0, 0.0));
- } else if (subtag_a[0] == "opentype_variation" || subtag_a[0] == "otv") {
- Dictionary variations;
- if (!subtag_a[1].is_empty()) {
- Vector<String> variation_tags = subtag_a[1].split(",");
- for (int j = 0; j < variation_tags.size(); j++) {
- Vector<String> subtag_b = variation_tags[j].split("=");
- _normalize_subtags(subtag_b);
-
- if (subtag_b.size() == 2) {
- variations[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float();
- }
- }
- fc->set_variation_opentype(variations);
+ OptionMap::Iterator name_option = bbcode_options.find("name");
+ if (!name_option) {
+ name_option = bbcode_options.find("n");
+ }
+ if (name_option) {
+ const String &fnt = name_option->value;
+ Ref<Font> font_data = ResourceLoader::load(fnt, "Font");
+ if (font_data.is_valid()) {
+ font = font_data;
+ def_font = CUSTOM_FONT;
+ }
+ }
+ OptionMap::Iterator size_option = bbcode_options.find("size");
+ if (!size_option) {
+ size_option = bbcode_options.find("s");
+ }
+ if (size_option) {
+ fnt_size = size_option->value.to_int();
+ }
+ OptionMap::Iterator glyph_spacing_option = bbcode_options.find("glyph_spacing");
+ if (!glyph_spacing_option) {
+ glyph_spacing_option = bbcode_options.find("gl");
+ }
+ if (glyph_spacing_option) {
+ int spacing = glyph_spacing_option->value.to_int();
+ fc->set_spacing(TextServer::SPACING_GLYPH, spacing);
+ }
+ OptionMap::Iterator space_spacing_option = bbcode_options.find("space_spacing");
+ if (!space_spacing_option) {
+ space_spacing_option = bbcode_options.find("sp");
+ }
+ if (space_spacing_option) {
+ int spacing = space_spacing_option->value.to_int();
+ fc->set_spacing(TextServer::SPACING_SPACE, spacing);
+ }
+ OptionMap::Iterator top_spacing_option = bbcode_options.find("top_spacing");
+ if (!top_spacing_option) {
+ top_spacing_option = bbcode_options.find("top");
+ }
+ if (top_spacing_option) {
+ int spacing = top_spacing_option->value.to_int();
+ fc->set_spacing(TextServer::SPACING_TOP, spacing);
+ }
+ OptionMap::Iterator bottom_spacing_option = bbcode_options.find("bottom_spacing");
+ if (!bottom_spacing_option) {
+ bottom_spacing_option = bbcode_options.find("bt");
+ }
+ if (bottom_spacing_option) {
+ int spacing = bottom_spacing_option->value.to_int();
+ fc->set_spacing(TextServer::SPACING_BOTTOM, spacing);
+ }
+ OptionMap::Iterator embolden_option = bbcode_options.find("embolden");
+ if (!embolden_option) {
+ embolden_option = bbcode_options.find("emb");
+ }
+ if (embolden_option) {
+ float emb = embolden_option->value.to_float();
+ fc->set_variation_embolden(emb);
+ }
+ OptionMap::Iterator face_index_option = bbcode_options.find("face_index");
+ if (!face_index_option) {
+ face_index_option = bbcode_options.find("fi");
+ }
+ if (face_index_option) {
+ int fi = face_index_option->value.to_int();
+ fc->set_variation_face_index(fi);
+ }
+ OptionMap::Iterator slant_option = bbcode_options.find("slant");
+ if (!slant_option) {
+ slant_option = bbcode_options.find("sln");
+ }
+ if (slant_option) {
+ float slant = slant_option->value.to_float();
+ fc->set_variation_transform(Transform2D(1.0, slant, 0.0, 1.0, 0.0, 0.0));
+ }
+ OptionMap::Iterator opentype_variation_option = bbcode_options.find("opentype_variation");
+ if (!opentype_variation_option) {
+ opentype_variation_option = bbcode_options.find("otv");
+ }
+ if (opentype_variation_option) {
+ Dictionary variations;
+ if (!opentype_variation_option->value.is_empty()) {
+ Vector<String> variation_tags = opentype_variation_option->value.split(",");
+ for (int j = 0; j < variation_tags.size(); j++) {
+ Vector<String> subtag_b = variation_tags[j].split("=");
+ _normalize_subtags(subtag_b);
+
+ if (subtag_b.size() == 2) {
+ variations[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float();
}
- } else if (subtag_a[0] == "opentype_features" || subtag_a[0] == "otf") {
- Dictionary features;
- if (!subtag_a[1].is_empty()) {
- Vector<String> feature_tags = subtag_a[1].split(",");
- for (int j = 0; j < feature_tags.size(); j++) {
- Vector<String> subtag_b = feature_tags[j].split("=");
- _normalize_subtags(subtag_b);
-
- if (subtag_b.size() == 2) {
- features[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float();
- } else if (subtag_b.size() == 1) {
- features[TS->name_to_tag(subtag_b[0])] = 1;
- }
- }
- fc->set_opentype_features(features);
+ }
+ fc->set_variation_opentype(variations);
+ }
+ }
+ OptionMap::Iterator opentype_features_option = bbcode_options.find("opentype_features");
+ if (!opentype_features_option) {
+ opentype_features_option = bbcode_options.find("otf");
+ }
+ if (opentype_features_option) {
+ Dictionary features;
+ if (!opentype_features_option->value.is_empty()) {
+ Vector<String> feature_tags = opentype_features_option->value.split(",");
+ for (int j = 0; j < feature_tags.size(); j++) {
+ Vector<String> subtag_b = feature_tags[j].split("=");
+ _normalize_subtags(subtag_b);
+
+ if (subtag_b.size() == 2) {
+ features[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float();
+ } else if (subtag_b.size() == 1) {
+ features[TS->name_to_tag(subtag_b[0])] = 1;
}
}
+ fc->set_opentype_features(features);
}
}
+
fc->set_base_font(font);
if (def_font != CUSTOM_FONT) {
@@ -4939,7 +5093,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front("font");
} else if (tag.begins_with("outline_size=")) {
- int fnt_size = tag.substr(13, tag.length()).to_int();
+ int fnt_size = _get_tag_value(tag).to_int();
if (fnt_size > 0) {
push_outline_size(fnt_size);
}
@@ -5078,7 +5232,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front("pulse");
set_process_internal(true);
} else if (tag.begins_with("bgcolor=")) {
- String color_str = tag.substr(8, tag.length()).unquote();
+ String color_str = _get_tag_value(tag).unquote();
Color color = Color::from_string(color_str, theme_cache.default_color);
push_bgcolor(color);
@@ -5086,7 +5240,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front("bgcolor");
} else if (tag.begins_with("fgcolor=")) {
- String color_str = tag.substr(8, tag.length()).unquote();
+ String color_str = _get_tag_value(tag).unquote();
Color color = Color::from_string(color_str, theme_cache.default_color);
push_fgcolor(color);
@@ -5115,17 +5269,6 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
}
-
- Vector<ItemFX *> fx_items;
- for (Item *E : main->subitems) {
- Item *subitem = static_cast<Item *>(E);
- _fetch_item_fx_stack(subitem, fx_items);
-
- if (fx_items.size()) {
- set_process_internal(true);
- break;
- }
- }
}
void RichTextLabel::scroll_to_selection() {
@@ -5726,19 +5869,89 @@ void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction)
if (text_direction != p_text_direction) {
text_direction = p_text_direction;
- main->first_invalid_line.store(0); //invalidate ALL
- _validate_line_caches();
+ if (!text.is_empty()) {
+ _apply_translation();
+ } else {
+ main->first_invalid_line.store(0); // Invalidate all lines.
+ _validate_line_caches();
+ }
+ queue_redraw();
+ }
+}
+
+Control::TextDirection RichTextLabel::get_text_direction() const {
+ return text_direction;
+}
+
+void RichTextLabel::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ _stop_thread();
+
+ if (default_alignment != p_alignment) {
+ default_alignment = p_alignment;
+ if (!text.is_empty()) {
+ _apply_translation();
+ } else {
+ main->first_invalid_line.store(0); // Invalidate all lines.
+ _validate_line_caches();
+ }
+ queue_redraw();
+ }
+}
+
+HorizontalAlignment RichTextLabel::get_horizontal_alignment() const {
+ return default_alignment;
+}
+
+void RichTextLabel::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
+ _stop_thread();
+
+ if (default_jst_flags != p_flags) {
+ default_jst_flags = p_flags;
+ if (!text.is_empty()) {
+ _apply_translation();
+ } else {
+ main->first_invalid_line.store(0); // Invalidate all lines.
+ _validate_line_caches();
+ }
queue_redraw();
}
}
+BitField<TextServer::JustificationFlag> RichTextLabel::get_justification_flags() const {
+ return default_jst_flags;
+}
+
+void RichTextLabel::set_tab_stops(const PackedFloat32Array &p_tab_stops) {
+ _stop_thread();
+
+ if (default_tab_stops != p_tab_stops) {
+ default_tab_stops = p_tab_stops;
+ if (!text.is_empty()) {
+ _apply_translation();
+ } else {
+ main->first_invalid_line.store(0); // Invalidate all lines.
+ _validate_line_caches();
+ }
+ queue_redraw();
+ }
+}
+
+PackedFloat32Array RichTextLabel::get_tab_stops() const {
+ return default_tab_stops;
+}
+
void RichTextLabel::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
_stop_thread();
st_parser = p_parser;
- main->first_invalid_line.store(0); //invalidate ALL
- _validate_line_caches();
+ if (!text.is_empty()) {
+ _apply_translation();
+ } else {
+ main->first_invalid_line.store(0); // Invalidate all lines.
+ _validate_line_caches();
+ }
queue_redraw();
}
}
@@ -5752,7 +5965,7 @@ void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) {
_stop_thread();
st_args = p_args;
- main->first_invalid_line.store(0); //invalidate ALL
+ main->first_invalid_line.store(0); // Invalidate all lines.
_validate_line_caches();
queue_redraw();
}
@@ -5762,17 +5975,17 @@ Array RichTextLabel::get_structured_text_bidi_override_options() const {
return st_args;
}
-Control::TextDirection RichTextLabel::get_text_direction() const {
- return text_direction;
-}
-
void RichTextLabel::set_language(const String &p_language) {
if (language != p_language) {
_stop_thread();
language = p_language;
- main->first_invalid_line.store(0); //invalidate ALL
- _validate_line_caches();
+ if (!text.is_empty()) {
+ _apply_translation();
+ } else {
+ main->first_invalid_line.store(0); // Invalidate all lines.
+ _validate_line_caches();
+ }
queue_redraw();
}
}
@@ -5786,7 +5999,7 @@ void RichTextLabel::set_autowrap_mode(TextServer::AutowrapMode p_mode) {
_stop_thread();
autowrap_mode = p_mode;
- main->first_invalid_line = 0; //invalidate ALL
+ main->first_invalid_line = 0; // Invalidate all lines.
_validate_line_caches();
queue_redraw();
}
@@ -5812,7 +6025,7 @@ void RichTextLabel::set_visible_ratio(float p_ratio) {
}
if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
- main->first_invalid_line.store(0); // Invalidate ALL.
+ main->first_invalid_line.store(0); // Invalidate all lines..
_validate_line_caches();
}
queue_redraw();
@@ -5940,6 +6153,13 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &RichTextLabel::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &RichTextLabel::get_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &RichTextLabel::set_justification_flags);
+ ClassDB::bind_method(D_METHOD("get_justification_flags"), &RichTextLabel::get_justification_flags);
+ ClassDB::bind_method(D_METHOD("set_tab_stops", "tab_stops"), &RichTextLabel::set_tab_stops);
+ ClassDB::bind_method(D_METHOD("get_tab_stops"), &RichTextLabel::get_tab_stops);
+
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &RichTextLabel::set_autowrap_mode);
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &RichTextLabel::get_autowrap_mode);
@@ -5994,7 +6214,10 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
- ClassDB::bind_method(D_METHOD("is_ready"), &RichTextLabel::is_ready);
+#ifndef DISABLE_DEPRECATED
+ ClassDB::bind_method(D_METHOD("is_ready"), &RichTextLabel::is_finished);
+#endif // DISABLE_DEPRECATED
+ ClassDB::bind_method(D_METHOD("is_finished"), &RichTextLabel::is_finished);
ClassDB::bind_method(D_METHOD("set_threaded", "threaded"), &RichTextLabel::set_threaded);
ClassDB::bind_method(D_METHOD("is_threaded"), &RichTextLabel::is_threaded);
@@ -6057,6 +6280,10 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "tab_stops"), "set_tab_stops", "get_tab_stops");
+
ADD_GROUP("Markup", "");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined");
@@ -6159,7 +6386,7 @@ void RichTextLabel::set_visible_characters_behavior(TextServer::VisibleCharacter
_stop_thread();
visible_chars_behavior = p_behavior;
- main->first_invalid_line.store(0); //invalidate ALL
+ main->first_invalid_line.store(0); // Invalidate all lines.
_validate_line_caches();
queue_redraw();
}
@@ -6179,7 +6406,7 @@ void RichTextLabel::set_visible_characters(int p_visible) {
}
}
if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
- main->first_invalid_line.store(0); //invalidate ALL
+ main->first_invalid_line.store(0); // Invalidate all lines.
_validate_line_caches();
}
queue_redraw();