diff options
Diffstat (limited to 'scene/gui/rich_text_label.cpp')
-rw-r--r-- | scene/gui/rich_text_label.cpp | 444 |
1 files changed, 312 insertions, 132 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 0b53f2e121..b15e75b1c0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -206,6 +206,49 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const { return s; } +void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) { + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); + + Line &l = p_frame->lines.write[p_line]; + + RID t = l.text_buf->get_rid(); + int spans = TS->shaped_get_span_count(t); + for (int i = 0; i < spans; i++) { + ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, i); + if (it) { + Ref<Font> font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + int font_size = _find_font_size(it); + if (font_size == -1) { + font_size = p_base_font_size; + } + Dictionary font_ftr = _find_font_features(it); + TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr); + } + } + + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + switch (it->type) { + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + for (Item *E : table->subitems) { + ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E); + for (int i = 0; i < frame->lines.size(); i++) { + _update_line_font(frame, i, p_base_font, p_base_font_size); + } + } + } break; + default: + break; + } + } +} + void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) { ERR_FAIL_COND(p_frame == nullptr); ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); @@ -239,7 +282,8 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames. ItemFrame *frame = static_cast<ItemFrame *>(E); for (int i = 0; i < frame->lines.size(); i++) { - _resize_line(frame, i, p_base_font, p_base_font_size, 1); + int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1; + _resize_line(frame, i, p_base_font, p_base_font_size, w); } idx++; } @@ -265,7 +309,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Assign actual widths. for (int i = 0; i < col_count; i++) { table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { + if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } table->total_width += table->columns[i].width + hseparation; @@ -326,13 +370,16 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); if (i > 0) { - frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y; + frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } else { frame->lines.write[i].offset.y = 0; } frame->lines.write[i].offset += offset; - float h = frame->lines[i].text_buf->get_size().y; + float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); + if (i > 0) { + h += get_theme_constant(SNAME("line_separation")); + } if (frame->min_size_over.y > 0) { h = MAX(h, frame->min_size_over.y); } @@ -363,7 +410,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> } if (p_line > 0) { - l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation")); + l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } else { l.offset.y = 0; } @@ -375,16 +422,31 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> Line &l = p_frame->lines.write[p_line]; + uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; + switch (autowrap_mode) { + case AUTOWRAP_WORD_SMART: + autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; + break; + case AUTOWRAP_WORD: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; + break; + case AUTOWRAP_ARBITRARY: + autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; + break; + case AUTOWRAP_OFF: + break; + } + // Clear cache. l.text_buf->clear(); - l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); + l.text_buf->set_flags(autowrap_flags | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); l.char_offset = *r_char_offset; l.char_count = 0; // Add indent. l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); l.text_buf->set_width(p_width - l.offset.x); - l.text_buf->set_align((HAlign)_find_align(l.from)); + l.text_buf->set_alignment(_find_alignment(l.from)); l.text_buf->set_direction(_find_direction(l.from)); if (tab_size > 0) { // Align inline tabs. @@ -398,7 +460,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; int remaining_characters = visible_characters - l.char_offset; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { - if (visible_characters >= 0 && remaining_characters <= 0) { + if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters <= 0) { break; } switch (it->type) { @@ -441,12 +503,12 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> Dictionary font_ftr = _find_font_features(it); String lang = _find_language(it); String tx = t->text; - if (visible_characters >= 0 && remaining_characters >= 0) { + if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0) { tx = tx.substr(0, remaining_characters); } remaining_characters -= tx.length(); - l.text_buf->add_string(tx, font, font_size, font_ftr, lang); + l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it); text += tx; l.char_count += tx.length(); } break; @@ -480,7 +542,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> int column = idx % col_count; for (int i = 0; i < frame->lines.size(); i++) { int char_offset = l.char_offset + l.char_count; - _shape_line(frame, i, p_base_font, p_base_font_size, 1, &char_offset); + int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1; + _shape_line(frame, i, p_base_font, p_base_font_size, w, &char_offset); int cell_ch = (char_offset - (l.char_offset + l.char_count)); l.char_count += cell_ch; t_char_count += cell_ch; @@ -510,7 +573,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Assign actual widths. for (int i = 0; i < col_count; i++) { table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { + if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } table->total_width += table->columns[i].width + hseparation; @@ -571,13 +634,16 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); if (i > 0) { - frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y; + frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } else { frame->lines.write[i].offset.y = 0; } frame->lines.write[i].offset += offset; - float h = frame->lines[i].text_buf->get_size().y; + float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); + if (i > 0) { + h += get_theme_constant(SNAME("line_separation")); + } if (frame->min_size_over.y > 0) { h = MAX(h, frame->min_size_over.y); } @@ -616,18 +682,19 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> *r_char_offset = l.char_offset + l.char_count; if (p_line > 0) { - l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation")); + l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } else { l.offset.y = 0; } } -int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs) { - Vector2 off; - +int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) { ERR_FAIL_COND_V(p_frame == nullptr, 0); ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0); + Vector2 off; + int line_spacing = get_theme_constant(SNAME("line_separation")); + Line &l = p_frame->lines.write[p_line]; Item *it_from = l.from; @@ -642,6 +709,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); bool lrtl = is_layout_rtl(); + bool trim_chars = (visible_characters >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING); + bool trim_glyphs_ltr = (visible_characters >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !lrtl)); + bool trim_glyphs_rtl = (visible_characters >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && lrtl)); + int total_glyphs = (trim_glyphs_ltr || trim_glyphs_rtl) ? get_total_glyph_count() : 0; + int visible_glyphs = total_glyphs * percent_visible; + Vector<int> list_index; Vector<ItemList *> list_items; _find_list(l.from, list_index, list_items); @@ -671,7 +744,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o prefix = segment + prefix; } } - if (prefix != "") { + if (!prefix.is_empty()) { Ref<Font> font = _find_font(l.from); if (font.is_null()) { font = get_theme_font(SNAME("normal_font")); @@ -685,13 +758,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (!lrtl && p_frame == main) { // Skip Scrollbar. offx -= scroll_w; } - font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HALIGN_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HORIZONTAL_ALIGNMENT_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color)); } else { float offx = 0.0f; if (lrtl && p_frame == main) { // Skip Scrollbar. offx += scroll_w; } - font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HALIGN_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HORIZONTAL_ALIGNMENT_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color)); } } @@ -707,6 +780,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Size2 ctrl_size = get_size(); // Draw text. for (int line = 0; line < l.text_buf->get_line_count(); line++) { + if (line > 0) { + off.y += line_spacing; + } + RID rid = l.text_buf->get_line_rid(line); if (p_ofs.y + off.y >= ctrl_size.height) { break; @@ -735,17 +812,17 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } // Draw text. - switch (l.text_buf->get_align()) { - case HALIGN_FILL: - case HALIGN_LEFT: { + switch (l.text_buf->get_alignment()) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { off.x += width - length; } } break; - case HALIGN_CENTER: { + case HORIZONTAL_ALIGNMENT_CENTER: { off.x += Math::floor((width - length) / 2.0); } break; - case HALIGN_RIGHT: { + case HORIZONTAL_ALIGNMENT_RIGHT: { if (!rtl) { off.x += width - length; } @@ -805,7 +882,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } for (int j = 0; j < 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); + _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++; } @@ -821,6 +898,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Vector2 gloff = off; // Draw oulines and shadow. + int processed_glyphs_ol = r_processed_glyphs; for (int i = 0; i < gl_size; i++) { Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start); int size = _find_outline_size(it, p_outline_size); @@ -948,7 +1026,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o // Draw glyph outlines. for (int j = 0; j < glyphs[i].repeat; j++) { if (visible) { - if (frid != RID()) { + bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); + if (!skip && frid != RID()) { if (font_shadow_color.a > 0) { TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color); } @@ -959,6 +1038,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color); } } + processed_glyphs_ol++; } gloff.x += glyphs[i].advance; } @@ -997,7 +1077,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o float y_off = TS->shaped_text_get_underline_position(rid); float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width); - } else if (_find_strikethrough(it)) { + } + if (_find_strikethrough(it)) { Color uc = font_color; uc.a *= 0.5; float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; @@ -1125,11 +1206,15 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o // Draw glyphs. for (int j = 0; j < glyphs[i].repeat; j++) { if (visible) { - if (frid != RID()) { - TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); - } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs)); + if (!skip) { + if (frid != RID()) { + TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + } } + r_processed_glyphs++; } off.x += glyphs[i].advance; } @@ -1164,7 +1249,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) { break; } from_line++; @@ -1177,7 +1262,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); while (ofs.y < size.height && from_line < main->lines.size()) { _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); - ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation")); + ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { if (r_outside != nullptr) { *r_outside = false; @@ -1215,17 +1300,17 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } } - switch (l.text_buf->get_align()) { - case HALIGN_FILL: - case HALIGN_LEFT: { + switch (l.text_buf->get_alignment()) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { if (rtl) { off.x += width - length; } } break; - case HALIGN_CENTER: { + case HORIZONTAL_ALIGNMENT_CENTER: { off.x += Math::floor((width - length) / 2.0); } break; - case HALIGN_RIGHT: { + case HORIZONTAL_ALIGNMENT_RIGHT: { if (!rtl) { off.x += width - length; } @@ -1296,7 +1381,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (rect.has_point(p_click) && !table_hit) { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); } - off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom(); + off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation")); } if (char_pos >= 0) { @@ -1422,9 +1507,12 @@ void RichTextLabel::_notification(int p_what) { update(); } break; - case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_THEME_CHANGED: { + main->first_invalid_font_line = 0; //invalidate ALL + update(); + } break; case NOTIFICATION_ENTER_TREE: { - if (text != "") { + if (!text.is_empty()) { set_text(text); } @@ -1460,7 +1548,7 @@ void RichTextLabel::_notification(int p_what) { //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) { break; } from_line++; @@ -1482,10 +1570,11 @@ void RichTextLabel::_notification(int p_what) { // 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 < main->lines.size()) { visible_paragraph_count++; - visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs); - ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation")); + visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs, processed_glyphs); + ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); from_line++; } } break; @@ -1521,6 +1610,10 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const return get_default_cursor_shape(); //invalid } + if (main->first_invalid_font_line < main->lines.size()) { + return get_default_cursor_shape(); //invalid + } + if (main->first_resized_line < main->lines.size()) { return get_default_cursor_shape(); //invalid } @@ -1545,6 +1638,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (main->first_invalid_line < main->lines.size()) { return; } + if (main->first_invalid_font_line < main->lines.size()) { + return; + } if (main->first_resized_line < main->lines.size()) { return; } @@ -1733,6 +1829,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (main->first_invalid_line < main->lines.size()) { return; } + if (main->first_invalid_font_line < main->lines.size()) { + return; + } if (main->first_resized_line < main->lines.size()) { return; } @@ -1979,19 +2078,19 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int return margin; } -RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) { +HorizontalAlignment RichTextLabel::_find_alignment(Item *p_item) { Item *item = p_item; while (item) { if (item->type == ITEM_PARAGRAPH) { ItemParagraph *p = static_cast<ItemParagraph *>(item); - return p->align; + return p->alignment; } item = item->parent; } - return default_align; + return default_alignment; } TextServer::Direction RichTextLabel::_find_direction(Item *p_item) { @@ -2185,27 +2284,32 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { if (p_frame->first_invalid_line == p_frame->lines.size()) { + Ref<Font> base_font = get_theme_font(SNAME("normal_font")); + int base_font_size = get_theme_font_size(SNAME("normal_font_size")); + + // Update fonts. + if (p_frame->first_invalid_font_line != p_frame->lines.size()) { + for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) { + _update_line_font(p_frame, i, base_font, base_font_size); + } + p_frame->first_resized_line = p_frame->first_invalid_font_line; + p_frame->first_invalid_font_line = p_frame->lines.size(); + } + if (p_frame->first_resized_line == p_frame->lines.size()) { return; } // Resize lines without reshaping. - Size2 size = get_size(); - if (fixed_width != -1) { - size.width = fixed_width; - } Rect2 text_rect = _get_text_rect(); - Ref<Font> base_font = get_theme_font(SNAME("normal_font")); - int base_font_size = get_theme_font_size(SNAME("normal_font_size")); - for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) { _resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w); } int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } p_frame->first_resized_line = p_frame->lines.size(); @@ -2214,21 +2318,17 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { vscroll->set_max(total_height); vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { - vscroll->set_value(total_height - size.height); + vscroll->set_value(total_height); } updating_scroll = false; if (fit_content_height) { - minimum_size_changed(); + update_minimum_size(); } return; } // Shape invalid lines. - Size2 size = get_size(); - if (fixed_width != -1) { - size.width = fixed_width; - } Rect2 text_rect = _get_text_rect(); Ref<Font> base_font = get_theme_font(SNAME("normal_font")); @@ -2241,22 +2341,23 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } p_frame->first_invalid_line = p_frame->lines.size(); p_frame->first_resized_line = p_frame->lines.size(); + p_frame->first_invalid_font_line = p_frame->lines.size(); updating_scroll = true; vscroll->set_max(total_height); vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { - vscroll->set_value(total_height - size.height); + vscroll->set_value(total_height); } updating_scroll = false; if (fit_content_height) { - minimum_size_changed(); + update_minimum_size(); } } @@ -2353,7 +2454,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) _invalidate_current_line(current_frame); if (fixed_width != -1) { - minimum_size_changed(); + update_minimum_size(); } } @@ -2378,9 +2479,10 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub // Then remove the provided item itself. p_item->parent->subitems.erase(p_item); } + memdelete(p_item); } -void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlign p_align) { +void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment) { if (current->type == ITEM_TABLE) { return; } @@ -2392,7 +2494,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, item->image = p_image; item->color = p_color; - item->inline_align = p_align; + item->inline_align = p_alignment; if (p_width > 0) { // custom width @@ -2584,11 +2686,11 @@ void RichTextLabel::push_strikethrough() { _add_item(item, true); } -void RichTextLabel::push_paragraph(Align p_align, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) { +void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) { ERR_FAIL_COND(current->type == ITEM_TABLE); ItemParagraph *item = memnew(ItemParagraph); - item->align = p_align; + item->alignment = p_alignment; item->direction = p_direction; item->language = p_language; item->st_parser = p_st_parser; @@ -2624,13 +2726,13 @@ void RichTextLabel::push_meta(const Variant &p_meta) { _add_item(item, true); } -void RichTextLabel::push_table(int p_columns, InlineAlign p_align) { +void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) { ERR_FAIL_COND(p_columns < 1); ItemTable *item = memnew(ItemTable); item->columns.resize(p_columns); item->total_width = 0; - item->inline_align = p_align; + item->inline_align = p_alignment; for (int i = 0; i < item->columns.size(); i++) { item->columns.write[i].expand = false; item->columns.write[i].expand_ratio = 1; @@ -2784,7 +2886,7 @@ void RichTextLabel::clear() { } if (fixed_width != -1) { - minimum_size_changed(); + update_minimum_size(); } } @@ -2801,7 +2903,7 @@ int RichTextLabel::get_tab_size() const { void RichTextLabel::set_fit_content_height(bool p_enabled) { if (p_enabled != fit_content_height) { fit_content_height = p_enabled; - minimum_size_changed(); + update_minimum_size(); } } @@ -2987,35 +3089,35 @@ void RichTextLabel::append_text(const String &p_bbcode) { columns = 1; } - int align = INLINE_ALIGN_TOP; + int alignment = INLINE_ALIGNMENT_TOP; if (subtag.size() > 2) { if (subtag[1] == "top" || subtag[1] == "t") { - align = INLINE_ALIGN_TOP_TO; + alignment = INLINE_ALIGNMENT_TOP_TO; } else if (subtag[1] == "center" || subtag[1] == "c") { - align = INLINE_ALIGN_CENTER_TO; + alignment = INLINE_ALIGNMENT_CENTER_TO; } else if (subtag[1] == "bottom" || subtag[1] == "b") { - align = INLINE_ALIGN_BOTTOM_TO; + alignment = INLINE_ALIGNMENT_BOTTOM_TO; } if (subtag[2] == "top" || subtag[2] == "t") { - align |= INLINE_ALIGN_TO_TOP; + alignment |= INLINE_ALIGNMENT_TO_TOP; } else if (subtag[2] == "center" || subtag[2] == "c") { - align |= INLINE_ALIGN_TO_CENTER; + alignment |= INLINE_ALIGNMENT_TO_CENTER; } else if (subtag[2] == "baseline" || subtag[2] == "l") { - align |= INLINE_ALIGN_TO_BASELINE; + alignment |= INLINE_ALIGNMENT_TO_BASELINE; } else if (subtag[2] == "bottom" || subtag[2] == "b") { - align |= INLINE_ALIGN_TO_BOTTOM; + alignment |= INLINE_ALIGNMENT_TO_BOTTOM; } } else if (subtag.size() > 1) { if (subtag[1] == "top" || subtag[1] == "t") { - align = INLINE_ALIGN_TOP; + alignment = INLINE_ALIGNMENT_TOP; } else if (subtag[1] == "center" || subtag[1] == "c") { - align = INLINE_ALIGN_CENTER; + alignment = INLINE_ALIGNMENT_CENTER; } else if (subtag[1] == "bottom" || subtag[1] == "b") { - align = INLINE_ALIGN_BOTTOM; + alignment = INLINE_ALIGNMENT_BOTTOM; } } - push_table(columns, (InlineAlign)align); + push_table(columns, (InlineAlignment)alignment); pos = brk_end + 1; tag_stack.push_front("table"); } else if (tag == "cell") { @@ -3079,6 +3181,12 @@ void RichTextLabel::append_text(const String &p_bbcode) { push_strikethrough(); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag == "lb") { + add_text("["); + pos = brk_end + 1; + } else if (tag == "rb") { + add_text("]"); + pos = brk_end + 1; } else if (tag == "lrm") { add_text(String::chr(0x200E)); pos = brk_end + 1; @@ -3128,15 +3236,15 @@ void RichTextLabel::append_text(const String &p_bbcode) { add_text(String::chr(0x00AD)); pos = brk_end + 1; } else if (tag == "center") { - push_paragraph(ALIGN_CENTER); + push_paragraph(HORIZONTAL_ALIGNMENT_CENTER); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "fill") { - push_paragraph(ALIGN_FILL); + push_paragraph(HORIZONTAL_ALIGNMENT_FILL); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "right") { - push_paragraph(ALIGN_RIGHT); + push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "ul") { @@ -3148,7 +3256,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { indent_level++; push_list(indent_level, LIST_NUMBERS, false); pos = brk_end + 1; - tag_stack.push_front(tag); + tag_stack.push_front("ol"); } else if (tag == "ol type=a") { indent_level++; push_list(indent_level, LIST_LETTERS, false); @@ -3175,12 +3283,12 @@ void RichTextLabel::append_text(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "p") { - push_paragraph(ALIGN_LEFT); + push_paragraph(HORIZONTAL_ALIGNMENT_LEFT); pos = brk_end + 1; tag_stack.push_front("p"); } else if (tag.begins_with("p ")) { Vector<String> subtag = tag.substr(2, tag.length()).split(" "); - Align align = ALIGN_LEFT; + HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED; String lang; Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; @@ -3189,13 +3297,13 @@ void RichTextLabel::append_text(const String &p_bbcode) { if (subtag_a.size() == 2) { if (subtag_a[0] == "align") { if (subtag_a[1] == "l" || subtag_a[1] == "left") { - align = ALIGN_LEFT; + alignment = HORIZONTAL_ALIGNMENT_LEFT; } else if (subtag_a[1] == "c" || subtag_a[1] == "center") { - align = ALIGN_CENTER; + alignment = HORIZONTAL_ALIGNMENT_CENTER; } else if (subtag_a[1] == "r" || subtag_a[1] == "right") { - align = ALIGN_RIGHT; + alignment = HORIZONTAL_ALIGNMENT_RIGHT; } else if (subtag_a[1] == "f" || subtag_a[1] == "fill") { - align = ALIGN_FILL; + alignment = HORIZONTAL_ALIGNMENT_FILL; } } else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") { if (subtag_a[1] == "a" || subtag_a[1] == "auto") { @@ -3226,7 +3334,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { } } } - push_paragraph(align, dir, lang, st_parser); + push_paragraph(alignment, dir, lang, st_parser); pos = brk_end + 1; tag_stack.push_front("p"); } else if (tag == "url") { @@ -3294,33 +3402,33 @@ void RichTextLabel::append_text(const String &p_bbcode) { pos = end; tag_stack.push_front(bbcode_name); } else if (tag.begins_with("img")) { - int align = INLINE_ALIGN_CENTER; + int alignment = INLINE_ALIGNMENT_CENTER; if (tag.begins_with("img=")) { Vector<String> subtag = tag.substr(4, tag.length()).split(","); if (subtag.size() > 1) { if (subtag[0] == "top" || subtag[0] == "t") { - align = INLINE_ALIGN_TOP_TO; + alignment = INLINE_ALIGNMENT_TOP_TO; } else if (subtag[0] == "center" || subtag[0] == "c") { - align = INLINE_ALIGN_CENTER_TO; + alignment = INLINE_ALIGNMENT_CENTER_TO; } else if (subtag[0] == "bottom" || subtag[0] == "b") { - align = INLINE_ALIGN_BOTTOM_TO; + alignment = INLINE_ALIGNMENT_BOTTOM_TO; } if (subtag[1] == "top" || subtag[1] == "t") { - align |= INLINE_ALIGN_TO_TOP; + alignment |= INLINE_ALIGNMENT_TO_TOP; } else if (subtag[1] == "center" || subtag[1] == "c") { - align |= INLINE_ALIGN_TO_CENTER; + alignment |= INLINE_ALIGNMENT_TO_CENTER; } else if (subtag[1] == "baseline" || subtag[1] == "l") { - align |= INLINE_ALIGN_TO_BASELINE; + alignment |= INLINE_ALIGNMENT_TO_BASELINE; } else if (subtag[1] == "bottom" || subtag[1] == "b") { - align |= INLINE_ALIGN_TO_BOTTOM; + alignment |= INLINE_ALIGNMENT_TO_BOTTOM; } } else if (subtag.size() > 0) { if (subtag[0] == "top" || subtag[0] == "t") { - align = INLINE_ALIGN_TOP; + alignment = INLINE_ALIGNMENT_TOP; } else if (subtag[0] == "center" || subtag[0] == "c") { - align = INLINE_ALIGN_CENTER; + alignment = INLINE_ALIGNMENT_CENTER; } else if (subtag[0] == "bottom" || subtag[0] == "b") { - align = INLINE_ALIGN_BOTTOM; + alignment = INLINE_ALIGNMENT_BOTTOM; } } } @@ -3362,7 +3470,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { } } - add_image(texture, width, height, color, (InlineAlign)align); + add_image(texture, width, height, color, (InlineAlignment)alignment); } pos = end; @@ -3905,7 +4013,7 @@ String RichTextLabel::get_selected_text() const { void RichTextLabel::selection_copy() { String text = get_selected_text(); - if (text != "") { + if (!text.is_empty()) { DisplayServer::get_singleton()->clipboard_set(text); } } @@ -4036,6 +4144,19 @@ String RichTextLabel::get_language() const { return language; } +void RichTextLabel::set_autowrap_mode(RichTextLabel::AutowrapMode p_mode) { + if (autowrap_mode != p_mode) { + autowrap_mode = p_mode; + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); + } +} + +RichTextLabel::AutowrapMode RichTextLabel::get_autowrap_mode() const { + return autowrap_mode; +} + void RichTextLabel::set_percent_visible(float p_percent) { if (percent_visible != p_percent) { if (p_percent < 0 || p_percent >= 1) { @@ -4045,8 +4166,10 @@ void RichTextLabel::set_percent_visible(float p_percent) { visible_characters = get_total_character_count() * p_percent; percent_visible = p_percent; } - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) { + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + } update(); } } @@ -4057,7 +4180,7 @@ float RichTextLabel::get_percent_visible() const { void RichTextLabel::set_effects(Array p_effects) { custom_effects = p_effects; - if ((text != "") && use_bbcode) { + if ((!text.is_empty()) && use_bbcode) { parse_bbcode(text); } } @@ -4072,7 +4195,7 @@ void RichTextLabel::install_effect(const Variant effect) { if (rteffect.is_valid()) { custom_effects.push_back(effect); - if ((text != "") && use_bbcode) { + if ((!text.is_empty()) && use_bbcode) { parse_bbcode(text); } } @@ -4081,14 +4204,22 @@ void RichTextLabel::install_effect(const Variant effect) { int RichTextLabel::get_content_height() const { int total_height = 0; if (main->lines.size()) { - total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y; + total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + main->lines[main->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } return total_height; } +int RichTextLabel::get_content_width() const { + int total_width = 0; + for (int i = 0; i < main->lines.size(); i++) { + total_width = MAX(total_width, main->lines[i].offset.x + main->lines[i].text_buf->get_size().x); + } + return total_width; +} + #ifndef DISABLE_DEPRECATED // People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0) -// Altough some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty +// Although some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty. bool RichTextLabel::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "bbcode_text" && !((String)p_value).is_empty()) { set_text(p_value); @@ -4102,7 +4233,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGN_CENTER)); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); @@ -4116,13 +4247,13 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color); ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size); ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color); - ClassDB::bind_method(D_METHOD("push_paragraph", "align", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT)); + ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT)); ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent); ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); 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_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGN_TOP)); + ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP)); 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"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); @@ -4145,6 +4276,9 @@ 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_autowrap_mode", "autowrap_mode"), &RichTextLabel::set_autowrap_mode); + ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &RichTextLabel::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline); ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined); @@ -4157,7 +4291,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow); ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following); - ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll); + ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &RichTextLabel::get_v_scroll_bar); ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line); ClassDB::bind_method(D_METHOD("scroll_to_paragraph", "paragraph"), &RichTextLabel::scroll_to_paragraph); @@ -4187,6 +4321,9 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters); ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters); + ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &RichTextLabel::get_visible_characters_behavior); + ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &RichTextLabel::set_visible_characters_behavior); + ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible); ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible); @@ -4202,6 +4339,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visible_paragraph_count"), &RichTextLabel::get_visible_paragraph_count); ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height); + ClassDB::bind_method(D_METHOD("get_content_width"), &RichTextLabel::get_content_width); ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values); @@ -4212,6 +4350,8 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); @@ -4230,7 +4370,9 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); @@ -4240,10 +4382,10 @@ void RichTextLabel::_bind_methods() { ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); - BIND_ENUM_CONSTANT(ALIGN_LEFT); - BIND_ENUM_CONSTANT(ALIGN_CENTER); - BIND_ENUM_CONSTANT(ALIGN_RIGHT); - BIND_ENUM_CONSTANT(ALIGN_FILL); + BIND_ENUM_CONSTANT(AUTOWRAP_OFF); + BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY); + BIND_ENUM_CONSTANT(AUTOWRAP_WORD); + BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART); BIND_ENUM_CONSTANT(LIST_NUMBERS); BIND_ENUM_CONSTANT(LIST_LETTERS); @@ -4276,6 +4418,25 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_META); BIND_ENUM_CONSTANT(ITEM_DROPCAP); BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); + + BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING); + BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING); + BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO); + BIND_ENUM_CONSTANT(VC_GLYPHS_LTR); + BIND_ENUM_CONSTANT(VC_GLYPHS_RTL); +} + +RichTextLabel::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const { + return visible_chars_behavior; +} + +void RichTextLabel::set_visible_characters_behavior(RichTextLabel::VisibleCharactersBehavior p_behavior) { + if (visible_chars_behavior != p_behavior) { + visible_chars_behavior = p_behavior; + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); + } } void RichTextLabel::set_visible_characters(int p_visible) { @@ -4289,8 +4450,10 @@ void RichTextLabel::set_visible_characters(int p_visible) { percent_visible = (float)p_visible / (float)total_char_count; } } - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) { + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + } update(); } } @@ -4318,9 +4481,25 @@ int RichTextLabel::get_total_character_count() const { return tc; } +int RichTextLabel::get_total_glyph_count() const { + int tg = 0; + Item *it = main; + while (it) { + if (it->type == ITEM_FRAME) { + ItemFrame *f = static_cast<ItemFrame *>(it); + for (int i = 0; i < f->lines.size(); i++) { + tg += TS->shaped_text_get_glyph_count(f->lines[i].text_buf->get_rid()); + } + } + it = _get_next_item(it, true); + } + + return tg; +} + void RichTextLabel::set_fixed_size_to_width(int p_width) { fixed_width = p_width; - minimum_size_changed(); + update_minimum_size(); } Size2 RichTextLabel::get_minimum_size() const { @@ -4331,7 +4510,7 @@ Size2 RichTextLabel::get_minimum_size() const { size.x += fixed_width; } - if (fixed_width != -1 || fit_content_height) { + if (fit_content_height) { const_cast<RichTextLabel *>(this)->_validate_line_caches(main); size.y += get_content_height(); } @@ -4480,6 +4659,7 @@ RichTextLabel::RichTextLabel() { main->lines.write[0].from = main; main->first_invalid_line = 0; main->first_resized_line = 0; + main->first_invalid_font_line = 0; current_frame = main; vscroll = memnew(VScrollBar); |