summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/RichTextLabel.xml27
-rw-r--r--editor/editor_about.cpp2
-rw-r--r--editor/editor_help.cpp45
-rw-r--r--modules/text_server_adv/text_server_adv.cpp5
-rw-r--r--modules/text_server_fb/text_server_fb.cpp5
-rw-r--r--scene/gui/rich_text_label.cpp829
-rw-r--r--scene/gui/rich_text_label.h50
-rw-r--r--scene/resources/text_paragraph.cpp138
-rw-r--r--scene/resources/text_paragraph.h6
9 files changed, 774 insertions, 333 deletions
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 62a692d4a7..c481fa30c2 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -60,6 +60,7 @@
<argument index="0" name="character" type="int" />
<description>
Returns the line number of the character position provided.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_character_paragraph">
@@ -67,24 +68,28 @@
<argument index="0" name="character" type="int" />
<description>
Returns the paragraph number of the character position provided.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_content_height" qualifiers="const">
<return type="int" />
<description>
Returns the height of the content.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_content_width" qualifiers="const">
<return type="int" />
<description>
Returns the width of the content.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_line_count" qualifiers="const">
<return type="int" />
<description>
Returns the total number of lines in the text. Wrapped text is counted as multiple lines.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_line_offset">
@@ -92,6 +97,7 @@
<argument index="0" name="line" type="int" />
<description>
Returns the vertical offset of the line found at the provided index.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_menu" qualifiers="const">
@@ -112,6 +118,7 @@
<argument index="0" name="paragraph" type="int" />
<description>
Returns the vertical offset of the paragraph found at the provided index.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_parsed_text" qualifiers="const">
@@ -155,12 +162,14 @@
<return type="int" />
<description>
Returns the number of visible lines.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="get_visible_paragraph_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of visible paragraphs. A paragraph is considered visible if at least one of its lines is visible.
+ [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_ready] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
<method name="install_effect">
@@ -176,6 +185,12 @@
Returns whether the menu is visible. Use this instead of [code]get_menu().visible[/code] to improve performance (so the creation of the menu is avoided).
</description>
</method>
+ <method name="is_ready" qualifiers="const">
+ <return type="bool" />
+ <description>
+ If [member threaded] is enabled, returns [code]true[/code] if the background thread has finished text processing, otherwise always return [code]true[/code].
+ </description>
+ </method>
<method name="newline">
<return type="void" />
<description>
@@ -476,6 +491,10 @@
The range of characters to display, as a [float] between 0.0 and 1.0. When assigned an out of range value, it's the same as assigning 1.0.
[b]Note:[/b] Setting this property updates [member visible_characters] based on current [method get_total_character_count].
</member>
+ <member name="progress_bar_delay" type="int" setter="set_progress_bar_delay" getter="get_progress_bar_delay" default="1000">
+ The delay after which the loading progress bar is displayed, in milliseconds. Set to [code]-1[/code] to disable progress bar entirely.
+ [b]Note:[/b] Progress bar is dislayed only if [member threaded] is enabled.
+ </member>
<member name="scroll_active" type="bool" setter="set_scroll_active" getter="is_scroll_active" default="true">
If [code]true[/code], the scrollbar is visible. Setting this to [code]false[/code] does not block scrolling completely. See [method scroll_to_line].
</member>
@@ -504,6 +523,9 @@
<member name="text_direction" type="int" setter="set_text_direction" getter="get_text_direction" enum="Control.TextDirection" default="0">
Base text writing direction.
</member>
+ <member name="threaded" type="bool" setter="set_threaded" getter="is_threaded" default="false">
+ If [code]true[/code], text processing is done in a background thread.
+ </member>
<member name="visible_characters" type="int" setter="set_visible_characters" getter="get_visible_characters" default="-1">
The restricted number of characters to display in the label. If [code]-1[/code], all characters will be displayed.
[b]Note:[/b] Setting this property updates [member percent_visible] based on current [method get_total_character_count].
@@ -513,6 +535,11 @@
</member>
</members>
<signals>
+ <signal name="finished">
+ <description>
+ Triggered when the document is fully loaded.
+ </description>
+ </signal>
<signal name="meta_clicked">
<argument index="0" name="meta" type="Variant" />
<description>
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index cd5a4f16e4..8e2aa3b35c 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -196,6 +196,7 @@ EditorAbout::EditorAbout() {
// License
_license_text = memnew(RichTextLabel);
+ _license_text->set_threaded(true);
_license_text->set_name(TTR("License"));
_license_text->set_h_size_flags(Control::SIZE_EXPAND_FILL);
_license_text->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -272,6 +273,7 @@ EditorAbout::EditorAbout() {
tpl_hbc->add_child(_tpl_tree);
_tpl_text = memnew(RichTextLabel);
+ _tpl_text->set_threaded(true);
_tpl_text->set_h_size_flags(Control::SIZE_EXPAND_FILL);
_tpl_text->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tpl_hbc->add_child(_tpl_text);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index e33d160762..d6278a63fe 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -235,7 +235,7 @@ String EditorHelp::_fix_constant(const String &p_constant) const {
}
void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) {
- method_line[p_method.name] = class_desc->get_line_count() - 2; //gets overridden if description
+ method_line[p_method.name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
const bool is_vararg = p_method.qualifiers.contains("vararg");
@@ -593,8 +593,8 @@ void EditorHelp::_update_doc() {
// Class description
if (!cd.description.is_empty()) {
- section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_line_count() - 2));
- description_line = class_desc->get_line_count() - 2;
+ section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_paragraph_count() - 2));
+ description_line = class_desc->get_paragraph_count() - 2;
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Description"));
@@ -664,7 +664,7 @@ void EditorHelp::_update_doc() {
}
if (has_properties) {
- section_line.push_back(Pair<String, int>(TTR("Properties"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Properties"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Properties"));
@@ -682,7 +682,7 @@ void EditorHelp::_update_doc() {
if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.is_empty()) {
continue;
}
- property_line[cd.properties[i].name] = class_desc->get_line_count() - 2; //gets overridden if description
+ property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
// Property type.
class_desc->push_cell();
@@ -828,7 +828,7 @@ void EditorHelp::_update_doc() {
cd.constructors.sort();
}
- section_line.push_back(Pair<String, int>(TTR("Constructors"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Constructors"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Constructors"));
@@ -839,7 +839,7 @@ void EditorHelp::_update_doc() {
if (sort_methods) {
methods.sort();
}
- section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Methods"));
@@ -851,7 +851,7 @@ void EditorHelp::_update_doc() {
cd.operators.sort();
}
- section_line.push_back(Pair<String, int>(TTR("Operators"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Operators"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Operators"));
@@ -860,7 +860,7 @@ void EditorHelp::_update_doc() {
// Theme properties
if (!cd.theme_properties.is_empty()) {
- section_line.push_back(Pair<String, int>(TTR("Theme Properties"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Theme Properties"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Theme Properties"));
@@ -882,7 +882,7 @@ void EditorHelp::_update_doc() {
data_type_names["style"] = TTR("Styles");
for (int i = 0; i < cd.theme_properties.size(); i++) {
- theme_property_line[cd.theme_properties[i].name] = class_desc->get_line_count() - 2; // Gets overridden if description.
+ theme_property_line[cd.theme_properties[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
if (theme_data_type != cd.theme_properties[i].data_type) {
theme_data_type = cd.theme_properties[i].data_type;
@@ -954,7 +954,7 @@ void EditorHelp::_update_doc() {
cd.signals.sort();
}
- section_line.push_back(Pair<String, int>(TTR("Signals"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Signals"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Signals"));
@@ -967,7 +967,7 @@ void EditorHelp::_update_doc() {
class_desc->push_indent(1);
for (int i = 0; i < cd.signals.size(); i++) {
- signal_line[cd.signals[i].name] = class_desc->get_line_count() - 2; // Gets overridden if description.
+ signal_line[cd.signals[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
class_desc->push_font(doc_code_font); // monofont
class_desc->push_color(headline_color);
@@ -1040,7 +1040,7 @@ void EditorHelp::_update_doc() {
// Enums
if (enums.size()) {
- section_line.push_back(Pair<String, int>(TTR("Enumerations"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Enumerations"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Enumerations"));
@@ -1051,7 +1051,7 @@ void EditorHelp::_update_doc() {
class_desc->add_newline();
for (KeyValue<String, Vector<DocData::ConstantDoc>> &E : enums) {
- enum_line[E.key] = class_desc->get_line_count() - 2;
+ enum_line[E.key] = class_desc->get_paragraph_count() - 2;
class_desc->push_font(doc_code_font);
class_desc->push_color(title_color);
@@ -1098,7 +1098,7 @@ void EditorHelp::_update_doc() {
}
// Add the enum constant line to the constant_line map so we can locate it as a constant.
- constant_line[enum_list[i].name] = class_desc->get_line_count() - 2;
+ constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2;
class_desc->push_font(doc_code_font);
class_desc->push_color(headline_color);
@@ -1144,7 +1144,7 @@ void EditorHelp::_update_doc() {
// Constants
if (constants.size()) {
- section_line.push_back(Pair<String, int>(TTR("Constants"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Constants"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Constants"));
@@ -1155,7 +1155,7 @@ void EditorHelp::_update_doc() {
class_desc->add_newline();
for (int i = 0; i < constants.size(); i++) {
- constant_line[constants[i].name] = class_desc->get_line_count() - 2;
+ constant_line[constants[i].name] = class_desc->get_paragraph_count() - 2;
class_desc->push_font(doc_code_font);
if (constants[i].value.begins_with("Color(") && constants[i].value.ends_with(")")) {
@@ -1205,7 +1205,7 @@ void EditorHelp::_update_doc() {
// Property descriptions
if (property_descr) {
- section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Property Descriptions"));
@@ -1220,7 +1220,7 @@ void EditorHelp::_update_doc() {
continue;
}
- property_line[cd.properties[i].name] = class_desc->get_line_count() - 2;
+ property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2;
class_desc->push_table(2);
class_desc->set_table_column_expand(1, true);
@@ -1371,7 +1371,7 @@ void EditorHelp::_update_doc() {
// Constructor descriptions
if (constructor_descriptions) {
- section_line.push_back(Pair<String, int>(TTR("Constructor Descriptions"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Constructor Descriptions"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Constructor Descriptions"));
@@ -1380,7 +1380,7 @@ void EditorHelp::_update_doc() {
// Method descriptions
if (method_descriptions) {
- section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Method Descriptions"));
@@ -1389,7 +1389,7 @@ void EditorHelp::_update_doc() {
// Operator descriptions
if (operator_descriptions) {
- section_line.push_back(Pair<String, int>(TTR("Operator Descriptions"), class_desc->get_line_count() - 2));
+ section_line.push_back(Pair<String, int>(TTR("Operator Descriptions"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->add_text(TTR("Operator Descriptions"));
@@ -1868,6 +1868,7 @@ EditorHelp::EditorHelp() {
class_desc = memnew(RichTextLabel);
add_child(class_desc);
+ class_desc->set_threaded(true);
class_desc->set_v_size_flags(SIZE_EXPAND_FILL);
class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4));
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index bd14b15561..ef7807ca5d 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -1691,6 +1691,8 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_
}
RID TextServerAdvanced::create_font() {
+ _THREAD_SAFE_METHOD_
+
FontDataAdvanced *fd = memnew(FontDataAdvanced);
return font_owner.make_rid(fd);
@@ -3359,6 +3361,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+
ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
sd->hb_buffer = hb_buffer_create();
sd->direction = p_direction;
@@ -3748,6 +3751,8 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
}
RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
+ _THREAD_SAFE_METHOD_
+
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index c53ca7766a..498b58175e 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -853,6 +853,8 @@ _FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_fo
}
RID TextServerFallback::create_font() {
+ _THREAD_SAFE_METHOD_
+
FontDataFallback *fd = memnew(FontDataFallback);
return font_owner.make_rid(fd);
@@ -2429,6 +2431,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
sd->orientation = p_orientation;
@@ -2807,6 +2810,8 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
}
RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
+ _THREAD_SAFE_METHOD_
+
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index fa0c2ce12c..78fd682c35 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -209,9 +209,10 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
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());
+ ERR_FAIL_COND(p_line < 0 || p_line >= (int)p_frame->lines.size());
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
+ MutexLock lock(l.text_buf->get_mutex());
RID t = l.text_buf->get_rid();
int spans = TS->shaped_get_span_count(t);
@@ -231,7 +232,7 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
}
}
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)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: {
@@ -239,7 +240,7 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
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++) {
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
_update_line_font(frame, i, p_base_font, p_base_font_size);
}
}
@@ -250,11 +251,12 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
}
}
-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());
+float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h) {
+ ERR_FAIL_COND_V(p_frame == nullptr, p_h);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), p_h);
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
+ MutexLock lock(l.text_buf->get_mutex());
l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
l.text_buf->set_width(p_width - l.offset.x);
@@ -265,7 +267,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
l.text_buf->tab_align(tabs);
}
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)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: {
@@ -275,16 +277,18 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int col_count = table->columns.size();
for (int i = 0; i < col_count; i++) {
- table->columns.write[i].width = 0;
+ table->columns[i].width = 0;
}
int idx = 0;
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++) {
+ float prev_h = 0;
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
+ MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
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);
+ prev_h = _resize_line(frame, i, p_base_font, p_base_font_size, w, prev_h);
}
idx++;
}
@@ -300,7 +304,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
for (int i = 0; i < col_count; i++) {
remaining_width -= table->columns[i].min_width;
if (table->columns[i].max_width > table->columns[i].min_width) {
- table->columns.write[i].expand = true;
+ table->columns[i].expand = true;
}
if (table->columns[i].expand) {
total_ratio += table->columns[i].expand_ratio;
@@ -309,9 +313,9 @@ 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;
+ table->columns[i].width = table->columns[i].min_width;
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->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
}
@@ -328,7 +332,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int dif = table->columns[i].width - table->columns[i].max_width;
if (dif > 0) {
table_need_fit = true;
- table->columns.write[i].width = table->columns[i].max_width;
+ table->columns[i].width = table->columns[i].max_width;
table->total_width -= dif;
total_ratio -= table->columns[i].expand_ratio;
}
@@ -342,7 +346,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (dif > 0) {
int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
int incr = MIN(dif, slice);
- table->columns.write[i].width += incr;
+ table->columns[i].width += incr;
table->total_width += incr;
}
}
@@ -366,16 +370,14 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
offset.x += frame->padding.position.x;
float yofs = frame->padding.position.y;
- for (int i = 0; i < frame->lines.size(); i++) {
- frame->lines.write[i].text_buf->set_width(table->columns[column].width);
- table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
+ float prev_h = 0;
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
+ MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
+ frame->lines[i].text_buf->set_width(table->columns[column].width);
+ table->columns[column].width = MAX(table->columns[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[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;
+ frame->lines[i].offset.y = prev_h;
+ frame->lines[i].offset += offset;
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) {
@@ -388,6 +390,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
h = MIN(h, frame->max_size_over.y);
}
yofs += h;
+ prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
yofs += frame->padding.size.y;
offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
@@ -410,18 +413,16 @@ 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 + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
- } else {
- l.offset.y = 0;
- }
+ l.offset.y = p_h;
+ return l.offset.y + l.text_buf->get_size().y + l.text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
-void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset) {
- ERR_FAIL_COND(p_frame == nullptr);
- ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h, int *r_char_offset) {
+ ERR_FAIL_COND_V(p_frame == nullptr, p_h);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), p_h);
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
+ MutexLock lock(l.text_buf->get_mutex());
uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
switch (autowrap_mode) {
@@ -458,7 +459,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Shape current paragraph.
String text;
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)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_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters <= 0) {
@@ -524,9 +525,9 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int t_char_count = 0;
// Set minimums to zero.
for (int i = 0; i < col_count; i++) {
- table->columns.write[i].min_width = 0;
- table->columns.write[i].max_width = 0;
- table->columns.write[i].width = 0;
+ table->columns[i].min_width = 0;
+ table->columns[i].max_width = 0;
+ table->columns[i].width = 0;
}
// Compute minimum width for each cell.
const int available_width = p_width - hseparation * (col_count - 1);
@@ -537,17 +538,20 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
ItemFrame *frame = static_cast<ItemFrame *>(E);
int column = idx % col_count;
- for (int i = 0; i < frame->lines.size(); i++) {
+ float prev_h = 0;
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
+ MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
+
int char_offset = l.char_offset + l.char_count;
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);
+ prev_h = _shape_line(frame, i, p_base_font, p_base_font_size, w, prev_h, &char_offset);
int cell_ch = (char_offset - (l.char_offset + l.char_count));
l.char_count += cell_ch;
t_char_count += cell_ch;
remaining_characters -= cell_ch;
- table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x));
- table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wrapped_size().x));
+ table->columns[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x));
+ table->columns[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wrapped_size().x));
}
idx++;
}
@@ -560,7 +564,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
for (int i = 0; i < col_count; i++) {
remaining_width -= table->columns[i].min_width;
if (table->columns[i].max_width > table->columns[i].min_width) {
- table->columns.write[i].expand = true;
+ table->columns[i].expand = true;
}
if (table->columns[i].expand) {
total_ratio += table->columns[i].expand_ratio;
@@ -569,9 +573,9 @@ 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;
+ table->columns[i].width = table->columns[i].min_width;
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->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
}
@@ -588,7 +592,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int dif = table->columns[i].width - table->columns[i].max_width;
if (dif > 0) {
table_need_fit = true;
- table->columns.write[i].width = table->columns[i].max_width;
+ table->columns[i].width = table->columns[i].max_width;
table->total_width -= dif;
total_ratio -= table->columns[i].expand_ratio;
}
@@ -602,7 +606,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (dif > 0) {
int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
int incr = MIN(dif, slice);
- table->columns.write[i].width += incr;
+ table->columns[i].width += incr;
table->total_width += incr;
}
}
@@ -626,16 +630,15 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
offset.x += frame->padding.position.x;
float yofs = frame->padding.position.y;
- for (int i = 0; i < frame->lines.size(); i++) {
- frame->lines.write[i].text_buf->set_width(table->columns[column].width);
- table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
+ float prev_h = 0;
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
+ MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
- 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[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;
+ frame->lines[i].text_buf->set_width(table->columns[column].width);
+ table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x));
+
+ frame->lines[i].offset.y = prev_h;
+ frame->lines[i].offset += offset;
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) {
@@ -648,6 +651,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
h = MIN(h, frame->max_size_over.y);
}
yofs += h;
+ prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
yofs += frame->padding.size.y;
offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
@@ -678,24 +682,22 @@ 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 + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
- } else {
- l.offset.y = 0;
- }
+ l.offset.y = p_h;
+ return l.offset.y + l.text_buf->get_size().y + l.text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
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);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), 0);
Vector2 off;
int line_spacing = get_theme_constant(SNAME("line_separation"));
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
+ MutexLock lock(l.text_buf->get_mutex());
Item *it_from = l.from;
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
if (it_from == nullptr) {
return 0;
@@ -877,7 +879,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
}
- for (int j = 0; j < frame->lines.size(); j++) {
+ 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++;
@@ -1299,22 +1301,12 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
int vofs = vscroll->get_value();
// Search for the first line.
- int from_line = 0;
-
- //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 + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) {
- break;
- }
- from_line++;
- }
-
- if (from_line >= main->lines.size()) {
- return;
- }
+ int to_line = main->first_invalid_line.load();
+ int from_line = _find_first_line(0, to_line, vofs);
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()) {
+ while (ofs.y < size.height && from_line < to_line) {
+ MutexLock lock(main->lines[from_line].text_buf->get_mutex());
_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 + 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))) {
@@ -1331,7 +1323,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
Vector2 off;
int char_pos = -1;
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
+ MutexLock lock(l.text_buf->get_mutex());
+
bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
bool lrtl = is_layout_rtl();
@@ -1420,14 +1414,14 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
}
if (crect.has_point(p_click)) {
- for (int j = 0; j < frame->lines.size(); j++) {
+ for (int j = 0; j < (int)frame->lines.size(); j++) {
_find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true);
if (table_click_frame && table_click_item) {
// Save cell detected cell hit data.
table_range = Vector2i(INT32_MAX, 0);
for (Item *F : table->subitems) {
ItemFrame *sub_frame = static_cast<ItemFrame *>(F);
- for (int k = 0; k < sub_frame->lines.size(); k++) {
+ for (int k = 0; k < (int)sub_frame->lines.size(); k++) {
table_range.x = MIN(table_range.x, sub_frame->lines[k].char_offset);
table_range.y = MAX(table_range.y, sub_frame->lines[k].char_offset + sub_frame->lines[k].char_count);
}
@@ -1484,7 +1478,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
// Find item.
if (r_click_item != nullptr) {
Item *it = p_frame->lines[p_line].from;
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
if (char_pos == p_frame->lines[p_line].char_count) {
// Selection after the end of line, select last item.
if (it_to != nullptr) {
@@ -1532,28 +1526,6 @@ void RichTextLabel::_scroll_changed(double) {
update();
}
-void RichTextLabel::_update_scroll() {
- int total_height = get_content_height();
-
- bool exceeds = total_height > get_size().height && scroll_active;
-
- if (exceeds != scroll_visible) {
- if (exceeds) {
- scroll_visible = true;
- scroll_w = vscroll->get_combined_minimum_size().width;
- vscroll->show();
- vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w);
- } else {
- scroll_visible = false;
- scroll_w = 0;
- vscroll->hide();
- }
-
- main->first_resized_line = 0; //invalidate ALL
- _validate_line_caches(main);
- }
-}
-
void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta_time) {
Item *it = p_frame;
while (it) {
@@ -1588,6 +1560,22 @@ void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta
}
}
+int RichTextLabel::_find_first_line(int p_from, int p_to, int p_vofs) const {
+ int l = p_from;
+ int r = p_to;
+ while (l < r) {
+ int m = Math::floor(double(l + r) / 2.0);
+ MutexLock lock(main->lines[m].text_buf->get_mutex());
+ int ofs = main->lines[m].offset.y + main->lines[m].text_buf->get_size().y + main->lines[m].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
+ if (ofs < p_vofs) {
+ l = m + 1;
+ } else {
+ r = m;
+ }
+ }
+ return l;
+}
+
void RichTextLabel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_MOUSE_EXIT: {
@@ -1600,38 +1588,45 @@ void RichTextLabel::_notification(int p_what) {
} break;
case NOTIFICATION_RESIZED: {
- main->first_resized_line = 0; //invalidate ALL
+ _stop_thread();
+ main->first_resized_line.store(0); //invalidate ALL
update();
} break;
case NOTIFICATION_THEME_CHANGED: {
- main->first_invalid_font_line = 0; //invalidate ALL
+ _stop_thread();
+ main->first_invalid_font_line.store(0); //invalidate ALL
update();
} break;
case NOTIFICATION_ENTER_TREE: {
+ _stop_thread();
if (!text.is_empty()) {
set_text(text);
}
- main->first_invalid_line = 0; //invalidate ALL
+ main->first_invalid_line.store(0); //invalidate ALL
update();
} break;
+ case NOTIFICATION_EXIT_TREE: {
+ _stop_thread();
+ } break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
- main->first_invalid_line = 0; //invalidate ALL
+ _stop_thread();
+ main->first_invalid_line.store(0); //invalidate ALL
update();
} break;
- case NOTIFICATION_DRAW: {
- _validate_line_caches(main);
- _update_scroll();
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ update();
+ } break;
+ case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
-
Size2 size = get_size();
- Rect2 text_rect = _get_text_rect();
draw_style_box(get_theme_stylebox(SNAME("normal")), Rect2(Point2(), size));
@@ -1641,22 +1636,42 @@ void RichTextLabel::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
}
+ // Start text shaping.
+ if (_validate_line_caches()) {
+ set_physics_process_internal(false); // Disable auto refresh, if text is fully processed.
+ } else {
+ // Draw loading progress bar.
+ if ((progress_delay > 0) && (OS::get_singleton()->get_ticks_msec() - loading_started >= (uint64_t)progress_delay)) {
+ Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"), SNAME("ProgressBar"));
+ Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"), SNAME("ProgressBar"));
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+
+ Vector2 p_size = Vector2(size.width - (style->get_offset().x + vscroll->get_combined_minimum_size().width) * 2, vscroll->get_combined_minimum_size().width);
+ Vector2 p_pos = Vector2(style->get_offset().x, size.height - style->get_offset().y - vscroll->get_combined_minimum_size().width);
+
+ draw_style_box(bg, Rect2(p_pos, p_size));
+
+ bool right_to_left = is_layout_rtl();
+ double r = loaded.load();
+ int mp = fg->get_minimum_size().width;
+ int p = round(r * (p_size.width - mp));
+ if (right_to_left) {
+ int p_remaining = round((1.0 - r) * (p_size.width - mp));
+ draw_style_box(fg, Rect2(p_pos + Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, p_size.height)));
+ } else {
+ draw_style_box(fg, Rect2(p_pos, Size2(p + fg->get_minimum_size().width, p_size.height)));
+ }
+ }
+ }
+
+ // Draw main text.
+ Rect2 text_rect = _get_text_rect();
float vofs = vscroll->get_value();
// Search for the first line.
- int from_line = 0;
-
- //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 + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) {
- break;
- }
- from_line++;
- }
+ int to_line = main->first_invalid_line.load();
+ int from_line = _find_first_line(0, to_line, vofs);
- if (from_line >= main->lines.size()) {
- break; //nothing to draw
- }
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
Color base_color = get_theme_color(SNAME("default_color"));
Color outline_color = get_theme_color(SNAME("font_outline_color"));
@@ -1671,7 +1686,9 @@ 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()) {
+ while (ofs.y < size.height && from_line < to_line) {
+ MutexLock lock(main->lines[from_line].text_buf->get_mutex());
+
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, 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"));
@@ -1681,6 +1698,9 @@ void RichTextLabel::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PROCESS: {
if (is_visible_in_tree()) {
+ if (!is_ready()) {
+ return;
+ }
double dt = get_process_delta_time();
_update_fx(main, dt);
update();
@@ -1708,18 +1728,6 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
return CURSOR_IBEAM;
}
- if (main->first_invalid_line < main->lines.size()) {
- 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
- }
-
Item *item = nullptr;
bool outside = true;
const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside);
@@ -1727,7 +1735,6 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
if (item && !outside && const_cast<RichTextLabel *>(this)->_find_meta(item, nullptr)) {
return CURSOR_POINTING_HAND;
}
-
return get_default_cursor_shape();
}
@@ -1737,16 +1744,6 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
- 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;
- }
-
if (b->get_button_index() == MouseButton::LEFT) {
if (b->is_pressed() && !b->is_double_click()) {
scroll_updated = false;
@@ -1800,6 +1797,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (c_frame) {
const Line &l = c_frame->lines[c_line];
+ MutexLock lock(l.text_buf->get_mutex());
PackedInt32Array words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid());
for (int i = 0; i < words.size(); i = i + 2) {
if (c_index >= words[i] && c_index < words[i + 1]) {
@@ -1945,18 +1943,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseMotion> m = p_event;
-
if (m.is_valid()) {
- 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;
- }
-
ItemFrame *c_frame = nullptr;
int c_line = 0;
Item *c_item = nullptr;
@@ -2434,93 +2421,215 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
return false;
}
-void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
- if (p_frame->first_invalid_line == p_frame->lines.size()) {
+void RichTextLabel::_thread_function(void *self) {
+ RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self);
+ rtl->_process_line_caches();
+ rtl->updating.store(false);
+ rtl->call_deferred(SNAME("update"));
+}
+
+void RichTextLabel::_stop_thread() {
+ if (threaded) {
+ stop_thread.store(true);
+ thread.wait_to_finish();
+ }
+}
+
+bool RichTextLabel::is_ready() const {
+ if (updating.load()) {
+ return false;
+ }
+ return (main->first_invalid_line.load() == (int)main->lines.size() && main->first_resized_line.load() == (int)main->lines.size() && main->first_invalid_font_line.load() == (int)main->lines.size());
+}
+
+void RichTextLabel::set_threaded(bool p_threaded) {
+ if (threaded != p_threaded) {
+ _stop_thread();
+ threaded = p_threaded;
+ update();
+ }
+}
+
+bool RichTextLabel::is_threaded() const {
+ return threaded;
+}
+
+void RichTextLabel::set_progress_bar_delay(int p_delay_ms) {
+ progress_delay = p_delay_ms;
+}
+
+int RichTextLabel::get_progress_bar_delay() const {
+ return progress_delay;
+}
+
+bool RichTextLabel::_validate_line_caches() {
+ if (updating.load()) {
+ return false;
+ }
+ if (main->first_invalid_line.load() == (int)main->lines.size()) {
+ MutexLock data_lock(data_mutex);
+ 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"));
+ int ctrl_height = get_size().height;
// 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);
+ if (main->first_invalid_font_line.load() != (int)main->lines.size()) {
+ for (int i = main->first_invalid_font_line.load(); i < (int)main->lines.size(); i++) {
+ _update_line_font(main, 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();
+ main->first_resized_line.store(main->first_invalid_font_line.load());
+ main->first_invalid_font_line.store(main->lines.size());
}
- if (p_frame->first_resized_line == p_frame->lines.size()) {
- return;
+ if (main->first_resized_line.load() == (int)main->lines.size()) {
+ return true;
}
// Resize lines without reshaping.
- Rect2 text_rect = _get_text_rect();
+ int fi = main->first_resized_line.load();
+
+ float total_height = 0;
+ for (int i = fi; i < (int)main->lines.size(); i++) {
+ total_height = _resize_line(main, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height);
+
+ updating_scroll = true;
+ bool exceeds = total_height > ctrl_height && scroll_active;
+ if (exceeds != scroll_visible) {
+ if (exceeds) {
+ scroll_visible = true;
+ scroll_w = vscroll->get_combined_minimum_size().width;
+ vscroll->show();
+ vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w);
+ } else {
+ scroll_visible = false;
+ scroll_w = 0;
+ vscroll->hide();
+ }
- 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);
- }
+ main->first_resized_line.store(0);
- 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 + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
- }
+ total_height = 0;
+ for (int j = 0; j <= i; j++) {
+ total_height = _resize_line(main, j, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height);
+
+ main->first_resized_line.store(j);
+ }
+ }
- p_frame->first_resized_line = p_frame->lines.size();
+ vscroll->set_max(total_height);
+ vscroll->set_page(text_rect.size.height);
+ if (scroll_follow && scroll_following) {
+ vscroll->set_value(total_height);
+ }
+ updating_scroll = false;
- 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);
+ main->first_resized_line.store(i);
}
- updating_scroll = false;
+
+ main->first_resized_line.store(main->lines.size());
if (fit_content_height) {
update_minimum_size();
}
- return;
+ return true;
}
+ stop_thread.store(false);
+ if (threaded) {
+ updating.store(true);
+ loaded.store(true);
+ thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this));
+ loading_started = OS::get_singleton()->get_ticks_msec();
+ set_physics_process_internal(true);
+ return false;
+ } else {
+ _process_line_caches();
+ update();
+ return true;
+ }
+}
+void RichTextLabel::_process_line_caches() {
// Shape invalid lines.
+ MutexLock data_lock(data_mutex);
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"));
+ int ctrl_height = get_size().height;
+ int fi = main->first_invalid_line.load();
+ int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count);
- int total_chars = (p_frame->first_invalid_line == 0) ? 0 : (p_frame->lines[p_frame->first_invalid_line].char_offset + p_frame->lines[p_frame->first_invalid_line].char_count);
- for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
- _shape_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, &total_chars);
- }
+ float total_height = 0;
+ for (int i = fi; i < (int)main->lines.size(); i++) {
+ total_height = _shape_line(main, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height, &total_chars);
- 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 + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
- }
+ updating_scroll = true;
+ bool exceeds = total_height > ctrl_height && scroll_active;
+ if (exceeds != scroll_visible) {
+ if (exceeds) {
+ scroll_visible = true;
+ scroll_w = vscroll->get_combined_minimum_size().width;
+ vscroll->show();
+ vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w);
+ } else {
+ scroll_visible = false;
+ scroll_w = 0;
+ vscroll->hide();
+ }
- 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();
+ main->first_invalid_line.store(0);
+ main->first_resized_line.store(0);
+ main->first_invalid_font_line.store(0);
- 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);
+ total_height = 0;
+ for (int j = 0; j <= i; j++) {
+ total_height = _resize_line(main, j, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height);
+
+ main->first_invalid_line.store(j);
+ main->first_resized_line.store(j);
+ main->first_invalid_font_line.store(j);
+ }
+ }
+
+ vscroll->set_max(total_height);
+ vscroll->set_page(text_rect.size.height);
+ if (scroll_follow && scroll_following) {
+ vscroll->set_value(total_height);
+ }
+ updating_scroll = false;
+
+ main->first_invalid_line.store(i);
+ main->first_resized_line.store(i);
+ main->first_invalid_font_line.store(i);
+
+ if (stop_thread.load()) {
+ return;
+ }
+ loaded.store(double(i) / double(main->lines.size()));
}
- updating_scroll = false;
+
+ main->first_invalid_line.store(main->lines.size());
+ main->first_resized_line.store(main->lines.size());
+ main->first_invalid_font_line.store(main->lines.size());
if (fit_content_height) {
update_minimum_size();
}
+ emit_signal(SNAME("finished"));
}
void RichTextLabel::_invalidate_current_line(ItemFrame *p_frame) {
- if (p_frame->lines.size() - 1 <= p_frame->first_invalid_line) {
- p_frame->first_invalid_line = p_frame->lines.size() - 1;
- update();
+ if ((int)p_frame->lines.size() - 1 <= p_frame->first_invalid_line) {
+ p_frame->first_invalid_line = (int)p_frame->lines.size() - 1;
}
}
void RichTextLabel::add_text(const String &p_text) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
if (current->type == ITEM_TABLE) {
return; //can't add anything here
}
@@ -2564,13 +2673,14 @@ void RichTextLabel::add_text(const String &p_text) {
_add_item(item, false);
current_frame->lines.resize(current_frame->lines.size() + 1);
if (item->type != ITEM_NEWLINE) {
- current_frame->lines.write[current_frame->lines.size() - 1].from = item;
+ current_frame->lines[current_frame->lines.size() - 1].from = item;
}
_invalidate_current_line(current_frame);
}
pos = end + 1;
}
+ update();
}
void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) {
@@ -2599,7 +2709,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
}
if (current_frame->lines[current_frame->lines.size() - 1].from == nullptr) {
- current_frame->lines.write[current_frame->lines.size() - 1].from = p_item;
+ current_frame->lines[current_frame->lines.size() - 1].from = p_item;
}
p_item->line = current_frame->lines.size() - 1;
@@ -2608,6 +2718,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
if (fixed_width != -1) {
update_minimum_size();
}
+ update();
}
void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_subitem_line) {
@@ -2635,6 +2746,9 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
}
void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
if (current->type == ITEM_TABLE) {
return;
}
@@ -2674,6 +2788,9 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
}
void RichTextLabel::add_newline() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
if (current->type == ITEM_TABLE) {
return;
}
@@ -2682,10 +2799,14 @@ void RichTextLabel::add_newline() {
_add_item(item, false);
current_frame->lines.resize(current_frame->lines.size() + 1);
_invalidate_current_line(current_frame);
+ update();
}
bool RichTextLabel::remove_line(const int p_line) {
- if (p_line >= current_frame->lines.size() || p_line < 0) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
+ if (p_line >= (int)current_frame->lines.size() || p_line < 0) {
return false;
}
@@ -2713,16 +2834,19 @@ bool RichTextLabel::remove_line(const int p_line) {
}
if (p_line == 0 && current->subitems.size() > 0) {
- main->lines.write[0].from = main;
+ main->lines[0].from = main;
}
- main->first_invalid_line = 0; // p_line ???
+ main->first_invalid_line.store(0);
update();
return true;
}
void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins, const Color &p_color, int p_ol_size, const Color &p_ol_color) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_string.is_empty());
ERR_FAIL_COND(p_font.is_null());
@@ -2741,6 +2865,9 @@ void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font
}
void RichTextLabel::push_font(const Ref<Font> &p_font) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_font.is_null());
ItemFont *item = memnew(ItemFont);
@@ -2785,6 +2912,9 @@ void RichTextLabel::push_mono() {
}
void RichTextLabel::push_font_size(int p_font_size) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemFontSize *item = memnew(ItemFontSize);
@@ -2793,6 +2923,9 @@ void RichTextLabel::push_font_size(int p_font_size) {
}
void RichTextLabel::push_font_features(const Dictionary &p_features) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemFontFeatures *item = memnew(ItemFontFeatures);
@@ -2801,6 +2934,9 @@ void RichTextLabel::push_font_features(const Dictionary &p_features) {
}
void RichTextLabel::push_outline_size(int p_font_size) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemOutlineSize *item = memnew(ItemOutlineSize);
@@ -2809,6 +2945,9 @@ void RichTextLabel::push_outline_size(int p_font_size) {
}
void RichTextLabel::push_color(const Color &p_color) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemColor *item = memnew(ItemColor);
@@ -2817,6 +2956,9 @@ void RichTextLabel::push_color(const Color &p_color) {
}
void RichTextLabel::push_outline_color(const Color &p_color) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemOutlineColor *item = memnew(ItemOutlineColor);
@@ -2825,6 +2967,9 @@ void RichTextLabel::push_outline_color(const Color &p_color) {
}
void RichTextLabel::push_underline() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemUnderline *item = memnew(ItemUnderline);
@@ -2832,6 +2977,9 @@ void RichTextLabel::push_underline() {
}
void RichTextLabel::push_strikethrough() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemStrikethrough *item = memnew(ItemStrikethrough);
@@ -2839,6 +2987,9 @@ void RichTextLabel::push_strikethrough() {
}
void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemParagraph *item = memnew(ItemParagraph);
@@ -2850,6 +3001,9 @@ void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::Tex
}
void RichTextLabel::push_indent(int p_level) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_level < 0);
@@ -2859,6 +3013,9 @@ void RichTextLabel::push_indent(int p_level) {
}
void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_level < 0);
@@ -2871,6 +3028,9 @@ void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) {
}
void RichTextLabel::push_meta(const Variant &p_meta) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemMeta *item = memnew(ItemMeta);
@@ -2879,6 +3039,9 @@ void RichTextLabel::push_meta(const Variant &p_meta) {
}
void RichTextLabel::push_hint(const String &p_string) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemHint *item = memnew(ItemHint);
@@ -2887,20 +3050,26 @@ void RichTextLabel::push_hint(const String &p_string) {
}
void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(p_columns < 1);
ItemTable *item = memnew(ItemTable);
item->columns.resize(p_columns);
item->total_width = 0;
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;
+ for (int i = 0; i < (int)item->columns.size(); i++) {
+ item->columns[i].expand = false;
+ item->columns[i].expand_ratio = 1;
}
_add_item(item, true, false);
}
void RichTextLabel::push_fade(int p_start_index, int p_length) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ItemFade *item = memnew(ItemFade);
item->starting_index = p_start_index;
item->length = p_length;
@@ -2908,6 +3077,9 @@ void RichTextLabel::push_fade(int p_start_index, int p_length) {
}
void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ItemShake *item = memnew(ItemShake);
item->strength = p_strength;
item->rate = p_rate;
@@ -2915,6 +3087,9 @@ void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
}
void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ItemWave *item = memnew(ItemWave);
item->frequency = p_frequency;
item->amplitude = p_amplitude;
@@ -2922,6 +3097,9 @@ void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0
}
void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ItemTornado *item = memnew(ItemTornado);
item->frequency = p_frequency;
item->radius = p_radius;
@@ -2929,6 +3107,9 @@ void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0
}
void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ItemRainbow *item = memnew(ItemRainbow);
item->frequency = p_frequency;
item->saturation = p_saturation;
@@ -2937,6 +3118,9 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq
}
void RichTextLabel::push_bgcolor(const Color &p_color) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemBGColor *item = memnew(ItemBGColor);
@@ -2945,6 +3129,9 @@ void RichTextLabel::push_bgcolor(const Color &p_color) {
}
void RichTextLabel::push_fgcolor(const Color &p_color) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemFGColor *item = memnew(ItemFGColor);
@@ -2953,6 +3140,9 @@ void RichTextLabel::push_fgcolor(const Color &p_color) {
}
void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ItemCustomFX *item = memnew(ItemCustomFX);
item->custom_effect = p_custom_effect;
item->char_fx_transform->environment = p_environment;
@@ -2960,15 +3150,23 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar
}
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type != ITEM_TABLE);
+
ItemTable *table = static_cast<ItemTable *>(current);
- ERR_FAIL_INDEX(p_column, table->columns.size());
- table->columns.write[p_column].expand = p_expand;
- table->columns.write[p_column].expand_ratio = p_ratio;
+ ERR_FAIL_INDEX(p_column, (int)table->columns.size());
+ table->columns[p_column].expand = p_expand;
+ table->columns[p_column].expand_ratio = p_ratio;
}
void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type != ITEM_FRAME);
+
ItemFrame *cell = static_cast<ItemFrame *>(current);
ERR_FAIL_COND(!cell->cell);
cell->odd_row_bg = p_odd_row_bg;
@@ -2976,14 +3174,22 @@ void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, con
}
void RichTextLabel::set_cell_border_color(const Color &p_color) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type != ITEM_FRAME);
+
ItemFrame *cell = static_cast<ItemFrame *>(current);
ERR_FAIL_COND(!cell->cell);
cell->border = p_color;
}
void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type != ITEM_FRAME);
+
ItemFrame *cell = static_cast<ItemFrame *>(current);
ERR_FAIL_COND(!cell->cell);
cell->min_size_over = p_min_size;
@@ -2991,13 +3197,20 @@ void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2
}
void RichTextLabel::set_cell_padding(const Rect2 &p_padding) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type != ITEM_FRAME);
+
ItemFrame *cell = static_cast<ItemFrame *>(current);
ERR_FAIL_COND(!cell->cell);
cell->padding = p_padding;
}
void RichTextLabel::push_cell() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(current->type != ITEM_TABLE);
ItemFrame *item = memnew(ItemFrame);
@@ -3006,20 +3219,23 @@ void RichTextLabel::push_cell() {
current_frame = item;
item->cell = true;
item->lines.resize(1);
- item->lines.write[0].from = nullptr;
- item->first_invalid_line = 0; // parent frame last line ???
+ item->lines[0].from = nullptr;
+ item->first_invalid_line.store(0); // parent frame last line ???
}
int RichTextLabel::get_current_table_column() const {
ERR_FAIL_COND_V(current->type != ITEM_TABLE, -1);
ItemTable *table = static_cast<ItemTable *>(current);
-
return table->subitems.size() % table->columns.size();
}
void RichTextLabel::pop() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
ERR_FAIL_COND(!current->parent);
+
if (current->type == ITEM_FRAME) {
current_frame = static_cast<ItemFrame *>(current)->parent_frame;
}
@@ -3027,12 +3243,15 @@ void RichTextLabel::pop() {
}
void RichTextLabel::clear() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
main->_clear_children();
current = main;
current_frame = main;
main->lines.clear();
main->lines.resize(1);
- main->first_invalid_line = 0;
+ main->first_invalid_line.store(0);
selection.click_frame = nullptr;
selection.click_item = nullptr;
@@ -3050,8 +3269,10 @@ void RichTextLabel::clear() {
}
void RichTextLabel::set_tab_size(int p_spaces) {
+ _stop_thread();
+
tab_size = p_spaces;
- main->first_resized_line = 0;
+ main->first_resized_line.store(0);
update();
}
@@ -3131,6 +3352,9 @@ void RichTextLabel::parse_bbcode(const String &p_bbcode) {
}
void RichTextLabel::append_text(const String &p_bbcode) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
int pos = 0;
List<String> tag_stack;
@@ -3871,9 +4095,13 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
void RichTextLabel::scroll_to_paragraph(int p_paragraph) {
- ERR_FAIL_INDEX(p_paragraph, main->lines.size());
- _validate_line_caches(main);
- vscroll->set_value(main->lines[p_paragraph].offset.y);
+ if (p_paragraph <= 0) {
+ vscroll->set_value(0);
+ } else if (p_paragraph >= main->first_invalid_line.load()) {
+ vscroll->set_value(vscroll->get_max());
+ } else {
+ vscroll->set_value(main->lines[p_paragraph].offset.y);
+ }
}
int RichTextLabel::get_paragraph_count() const {
@@ -3888,10 +4116,14 @@ int RichTextLabel::get_visible_paragraph_count() const {
}
void RichTextLabel::scroll_to_line(int p_line) {
- _validate_line_caches(main);
-
+ if (p_line <= 0) {
+ vscroll->set_value(0);
+ return;
+ }
int line_count = 0;
- for (int i = 0; i < main->lines.size(); i++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
+ MutexLock lock(main->lines[i].text_buf->get_mutex());
if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) {
float line_offset = 0.f;
for (int j = 0; j < p_line - line_count; j++) {
@@ -3902,11 +4134,14 @@ void RichTextLabel::scroll_to_line(int p_line) {
}
line_count += main->lines[i].text_buf->get_line_count();
}
+ vscroll->set_value(vscroll->get_max());
}
float RichTextLabel::get_line_offset(int p_line) {
int line_count = 0;
- for (int i = 0; i < main->lines.size(); i++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
+ MutexLock lock(main->lines[i].text_buf->get_mutex());
if ((line_count <= p_line) && (p_line <= line_count + main->lines[i].text_buf->get_line_count())) {
float line_offset = 0.f;
for (int j = 0; j < p_line - line_count; j++) {
@@ -3920,7 +4155,8 @@ float RichTextLabel::get_line_offset(int p_line) {
}
float RichTextLabel::get_paragraph_offset(int p_paragraph) {
- if (0 <= p_paragraph && p_paragraph < main->lines.size()) {
+ int to_line = main->first_invalid_line.load();
+ if (0 <= p_paragraph && p_paragraph < to_line) {
return main->lines[p_paragraph].offset.y;
}
return 0;
@@ -3928,7 +4164,9 @@ float RichTextLabel::get_paragraph_offset(int p_paragraph) {
int RichTextLabel::get_line_count() const {
int line_count = 0;
- for (int i = 0; i < main->lines.size(); i++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
+ MutexLock lock(main->lines[i].text_buf->get_mutex());
line_count += main->lines[i].text_buf->get_line_count();
}
return line_count;
@@ -3989,13 +4227,13 @@ bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_f
ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E->get());
if (p_reverse_search) {
- for (int i = frame->lines.size() - 1; i >= 0; i--) {
+ for (int i = (int)frame->lines.size() - 1; i >= 0; i--) {
if (_search_line(frame, i, p_string, -1, p_reverse_search)) {
return true;
}
}
} else {
- for (int i = 0; i < frame->lines.size(); i++) {
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
if (_search_line(frame, i, p_string, 0, p_reverse_search)) {
return true;
}
@@ -4008,12 +4246,12 @@ bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_f
bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search) {
ERR_FAIL_COND_V(p_frame == nullptr, false);
- ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), false);
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
String text;
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)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_NEWLINE: {
@@ -4071,7 +4309,8 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
int char_idx = p_search_previous ? -1 : 0;
int current_line = 0;
- int ending_line = main->lines.size() - 1;
+ int to_line = main->first_invalid_line.load();
+ int ending_line = to_line - 1;
if (p_from_selection && selection.active) {
// First check to see if other results exist in current line
char_idx = p_search_previous ? selection.from_char - 1 : selection.to_char;
@@ -4120,8 +4359,8 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
while (current_line != ending_line) {
// Wrap around
if (current_line < 0) {
- current_line = main->lines.size() - 1;
- } else if (current_line >= main->lines.size()) {
+ current_line = to_line - 1;
+ } else if (current_line >= to_line) {
current_line = 0;
}
@@ -4143,12 +4382,13 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) const {
String text;
+
ERR_FAIL_COND_V(p_frame == nullptr, text);
- ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), text);
- Line &l = p_frame->lines.write[p_line];
+ Line &l = p_frame->lines[p_line];
- Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
int end_idx = 0;
if (it_to != nullptr) {
end_idx = it_to->index;
@@ -4163,7 +4403,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p
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++) {
+ for (int i = 0; i < (int)frame->lines.size(); i++) {
text += _get_line_text(frame, i, p_selection);
}
}
@@ -4227,7 +4467,8 @@ String RichTextLabel::get_selected_text() const {
}
String text;
- for (int i = 0; i < main->lines.size(); i++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
text += _get_line_text(main, i, selection);
}
return text;
@@ -4368,19 +4609,23 @@ String RichTextLabel::get_parsed_text() const {
void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+ _stop_thread();
+
if (text_direction != p_text_direction) {
text_direction = p_text_direction;
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
update();
}
}
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 = 0; //invalidate ALL
- _validate_line_caches(main);
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
update();
}
}
@@ -4390,10 +4635,14 @@ TextServer::StructuredTextParser RichTextLabel::get_structured_text_bidi_overrid
}
void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) {
- st_args = p_args;
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
- update();
+ if (st_args != p_args) {
+ _stop_thread();
+
+ st_args = p_args;
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
+ update();
+ }
}
Array RichTextLabel::get_structured_text_bidi_override_options() const {
@@ -4406,9 +4655,11 @@ Control::TextDirection RichTextLabel::get_text_direction() const {
void RichTextLabel::set_language(const String &p_language) {
if (language != p_language) {
+ _stop_thread();
+
language = p_language;
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
update();
}
}
@@ -4419,9 +4670,11 @@ String RichTextLabel::get_language() const {
void RichTextLabel::set_autowrap_mode(RichTextLabel::AutowrapMode p_mode) {
if (autowrap_mode != p_mode) {
+ _stop_thread();
+
autowrap_mode = p_mode;
main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ _validate_line_caches();
update();
}
}
@@ -4432,6 +4685,8 @@ RichTextLabel::AutowrapMode RichTextLabel::get_autowrap_mode() const {
void RichTextLabel::set_percent_visible(float p_percent) {
if (percent_visible != p_percent) {
+ _stop_thread();
+
if (p_percent < 0 || p_percent >= 1) {
visible_characters = -1;
percent_visible = 1;
@@ -4440,8 +4695,8 @@ void RichTextLabel::set_percent_visible(float p_percent) {
percent_visible = p_percent;
}
if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
}
update();
}
@@ -4476,15 +4731,19 @@ 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 + main->lines[main->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
+ int to_line = main->first_invalid_line.load();
+ if (to_line) {
+ MutexLock lock(main->lines[to_line - 1].text_buf->get_mutex());
+ total_height = main->lines[to_line - 1].offset.y + main->lines[to_line - 1].text_buf->get_size().y + main->lines[to_line - 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++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
+ MutexLock lock(main->lines[i].text_buf->get_mutex());
total_width = MAX(total_width, main->lines[i].offset.x + main->lines[i].text_buf->get_size().x);
}
return total_width;
@@ -4603,6 +4862,14 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
+ ClassDB::bind_method(D_METHOD("is_ready"), &RichTextLabel::is_ready);
+
+ ClassDB::bind_method(D_METHOD("set_threaded", "threaded"), &RichTextLabel::set_threaded);
+ ClassDB::bind_method(D_METHOD("is_threaded"), &RichTextLabel::is_threaded);
+
+ ClassDB::bind_method(D_METHOD("set_progress_bar_delay", "delay_ms"), &RichTextLabel::set_progress_bar_delay);
+ ClassDB::bind_method(D_METHOD("get_progress_bar_delay"), &RichTextLabel::get_progress_bar_delay);
+
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);
@@ -4643,6 +4910,9 @@ void RichTextLabel::_bind_methods() {
// Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded"), "set_threaded", "is_threaded");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "progress_bar_delay"), "set_progress_bar_delay", "get_progress_bar_delay");
+
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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled");
@@ -4676,6 +4946,8 @@ 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)));
+ ADD_SIGNAL(MethodInfo("finished"));
+
BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
@@ -4727,15 +4999,19 @@ RichTextLabel::VisibleCharactersBehavior RichTextLabel::get_visible_characters_b
void RichTextLabel::set_visible_characters_behavior(RichTextLabel::VisibleCharactersBehavior p_behavior) {
if (visible_chars_behavior != p_behavior) {
+ _stop_thread();
+
visible_chars_behavior = p_behavior;
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
update();
}
}
void RichTextLabel::set_visible_characters(int p_visible) {
if (visible_characters != p_visible) {
+ _stop_thread();
+
visible_characters = p_visible;
if (p_visible == -1) {
percent_visible = 1;
@@ -4746,8 +5022,8 @@ void RichTextLabel::set_visible_characters(int p_visible) {
}
}
if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ main->first_invalid_line.store(0); //invalidate ALL
+ _validate_line_caches();
}
update();
}
@@ -4759,7 +5035,9 @@ int RichTextLabel::get_visible_characters() const {
int RichTextLabel::get_character_line(int p_char) {
int line_count = 0;
- for (int i = 0; i < main->lines.size(); i++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
+ MutexLock lock(main->lines[i].text_buf->get_mutex());
if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) {
Vector2i range = main->lines[i].text_buf->get_line_range(j);
@@ -4777,7 +5055,8 @@ int RichTextLabel::get_character_line(int p_char) {
int RichTextLabel::get_character_paragraph(int p_char) {
int para_count = 0;
- for (int i = 0; i < main->lines.size(); i++) {
+ int to_line = main->first_invalid_line.load();
+ for (int i = 0; i < to_line; i++) {
if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
return para_count;
} else {
@@ -4802,7 +5081,6 @@ int RichTextLabel::get_total_character_count() const {
}
it = _get_next_item(it, true);
}
-
return tc;
}
@@ -4812,7 +5090,8 @@ int RichTextLabel::get_total_glyph_count() const {
while (it) {
if (it->type == ITEM_FRAME) {
ItemFrame *f = static_cast<ItemFrame *>(it);
- for (int i = 0; i < f->lines.size(); i++) {
+ for (int i = 0; i < (int)f->lines.size(); i++) {
+ MutexLock lock(f->lines[i].text_buf->get_mutex());
tg += TS->shaped_text_get_glyph_count(f->lines[i].text_buf->get_rid());
}
}
@@ -4836,7 +5115,6 @@ Size2 RichTextLabel::get_minimum_size() const {
}
if (fit_content_height) {
- const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
size.y += get_content_height();
}
@@ -5034,10 +5312,10 @@ RichTextLabel::RichTextLabel(const String &p_text) {
main->index = 0;
current = main;
main->lines.resize(1);
- main->lines.write[0].from = main;
- main->first_invalid_line = 0;
- main->first_resized_line = 0;
- main->first_invalid_font_line = 0;
+ main->lines[0].from = main;
+ main->first_invalid_line.store(0);
+ main->first_resized_line.store(0);
+ main->first_invalid_font_line.store(0);
current_frame = main;
vscroll = memnew(VScrollBar);
@@ -5052,10 +5330,13 @@ RichTextLabel::RichTextLabel(const String &p_text) {
vscroll->hide();
set_text(p_text);
+ updating.store(false);
+ stop_thread.store(false);
set_clip_contents(true);
}
RichTextLabel::~RichTextLabel() {
+ _stop_thread();
memdelete(main);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index c6d0d0875d..7fbd5f1745 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -141,10 +141,10 @@ private:
struct ItemFrame : public Item {
bool cell = false;
- Vector<Line> lines;
- int first_invalid_line = 0;
- int first_invalid_font_line = 0;
- int first_resized_line = 0;
+ LocalVector<Line> lines;
+ std::atomic<int> first_invalid_line;
+ std::atomic<int> first_invalid_font_line;
+ std::atomic<int> first_resized_line;
ItemFrame *parent_frame = nullptr;
@@ -155,7 +155,12 @@ private:
Size2 max_size_over = Size2(-1, -1);
Rect2 padding;
- ItemFrame() { type = ITEM_FRAME; }
+ ItemFrame() {
+ type = ITEM_FRAME;
+ first_invalid_line.store(0);
+ first_invalid_font_line.store(0);
+ first_resized_line.store(0);
+ }
};
struct ItemText : public Item {
@@ -263,8 +268,8 @@ private:
int width = 0;
};
- Vector<Column> columns;
- Vector<float> rows;
+ LocalVector<Column> columns;
+ LocalVector<float> rows;
int total_width = 0;
int total_height = 0;
@@ -363,6 +368,16 @@ private:
Item *current = nullptr;
ItemFrame *current_frame = nullptr;
+ Thread thread;
+ Mutex data_mutex;
+ bool threaded = false;
+ std::atomic<bool> stop_thread;
+ std::atomic<bool> updating;
+ std::atomic<double> loaded;
+
+ uint64_t loading_started = 0;
+ int progress_delay = 1000;
+
VScrollBar *vscroll = nullptr;
AutowrapMode autowrap_mode = AUTOWRAP_WORD_SMART;
@@ -392,7 +407,11 @@ private:
Array custom_effects;
void _invalidate_current_line(ItemFrame *p_frame);
- void _validate_line_caches(ItemFrame *p_frame);
+
+ static void _thread_function(void *self);
+ void _stop_thread();
+ bool _validate_line_caches();
+ void _process_line_caches();
void _add_item(Item *p_item, bool p_enter = false, bool p_ensure_newline = false);
void _remove_item(Item *p_item, const int p_line, const int p_subitem_line);
@@ -446,8 +465,9 @@ private:
bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search);
bool _search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search);
- void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
- void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
+ float _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h, int *r_char_offset);
+ float _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h);
+
void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
int _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);
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false);
@@ -480,9 +500,9 @@ private:
bool _find_layout_subitem(Item *from, Item *to);
void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack);
- void _update_scroll();
void _update_fx(ItemFrame *p_frame, double p_delta_time);
void _scroll_changed(double);
+ int _find_first_line(int p_from, int p_to, int p_vofs) const;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
virtual String get_tooltip(const Point2 &p_pos) const override;
@@ -611,6 +631,14 @@ public:
bool is_deselect_on_focus_loss_enabled() const;
void deselect();
+ bool is_ready() const;
+
+ void set_threaded(bool p_threaded);
+ bool is_threaded() const;
+
+ void set_progress_bar_delay(int p_delay_ms);
+ int get_progress_bar_delay() const;
+
// Context menu.
PopupMenu *get_menu() const;
bool is_menu_visible() const;
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 61adaf43dd..477b41efaa 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -139,7 +139,7 @@ void TextParagraph::_bind_methods() {
void TextParagraph::_shape_lines() {
if (lines_dirty) {
- for (int i = 0; i < lines_rid.size(); i++) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
@@ -218,14 +218,14 @@ void TextParagraph::_shape_lines() {
// Fill after min_size calculation.
if (autowrap_enabled) {
- int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
- bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size();
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
+ bool lines_hidden = visible_lines > 0 && visible_lines < (int)lines_rid.size();
if (lines_hidden) {
overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS;
}
if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
- for (int i = 0; i < lines_rid.size(); i++) {
- if (i < visible_lines - 1 || lines_rid.size() == 1) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
+ if (i < visible_lines - 1 || (int)lines_rid.size() == 1) {
TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
} else if (i == (visible_lines - 1)) {
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
@@ -238,7 +238,7 @@ void TextParagraph::_shape_lines() {
} else {
// Autowrap disabled.
- for (int i = 0; i < lines_rid.size(); i++) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
@@ -258,8 +258,10 @@ RID TextParagraph::get_rid() const {
}
RID TextParagraph::get_line_rid(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), RID());
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), RID());
return lines_rid[p_line];
}
@@ -268,9 +270,11 @@ RID TextParagraph::get_dropcap_rid() const {
}
void TextParagraph::clear() {
+ _THREAD_SAFE_METHOD_
+
spacing_top = 0;
spacing_bottom = 0;
- for (int i = 0; i < lines_rid.size(); i++) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
@@ -279,57 +283,79 @@ void TextParagraph::clear() {
}
void TextParagraph::set_preserve_invalid(bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+
TS->shaped_text_set_preserve_invalid(rid, p_enabled);
TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled);
lines_dirty = true;
}
bool TextParagraph::get_preserve_invalid() const {
+ _THREAD_SAFE_METHOD_
+
return TS->shaped_text_get_preserve_invalid(rid);
}
void TextParagraph::set_preserve_control(bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+
TS->shaped_text_set_preserve_control(rid, p_enabled);
TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled);
lines_dirty = true;
}
bool TextParagraph::get_preserve_control() const {
+ _THREAD_SAFE_METHOD_
+
return TS->shaped_text_get_preserve_control(rid);
}
void TextParagraph::set_direction(TextServer::Direction p_direction) {
+ _THREAD_SAFE_METHOD_
+
TS->shaped_text_set_direction(rid, p_direction);
TS->shaped_text_set_direction(dropcap_rid, p_direction);
lines_dirty = true;
}
TextServer::Direction TextParagraph::get_direction() const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
return TS->shaped_text_get_direction(rid);
}
void TextParagraph::set_custom_punctuation(const String &p_punct) {
+ _THREAD_SAFE_METHOD_
+
TS->shaped_text_set_custom_punctuation(rid, p_punct);
lines_dirty = true;
}
String TextParagraph::get_custom_punctuation() const {
+ _THREAD_SAFE_METHOD_
+
return TS->shaped_text_get_custom_punctuation(rid);
}
void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+
TS->shaped_text_set_orientation(rid, p_orientation);
TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
lines_dirty = true;
}
TextServer::Orientation TextParagraph::get_orientation() const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
return TS->shaped_text_get_orientation(rid);
}
bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) {
+ _THREAD_SAFE_METHOD_
+
ERR_FAIL_COND_V(p_fonts.is_null(), false);
TS->shaped_text_clear(dropcap_rid);
dropcap_margins = p_dropcap_margins;
@@ -339,12 +365,16 @@ bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts,
}
void TextParagraph::clear_dropcap() {
+ _THREAD_SAFE_METHOD_
+
dropcap_margins = Rect2();
TS->shaped_text_clear(dropcap_rid);
lines_dirty = true;
}
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
+ _THREAD_SAFE_METHOD_
+
ERR_FAIL_COND_V(p_fonts.is_null(), false);
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
@@ -362,23 +392,31 @@ int TextParagraph::get_spacing_bottom() const {
}
void TextParagraph::set_bidi_override(const Array &p_override) {
+ _THREAD_SAFE_METHOD_
+
TS->shaped_text_set_bidi_override(rid, p_override);
lines_dirty = true;
}
bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
+ _THREAD_SAFE_METHOD_
+
bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
lines_dirty = true;
return res;
}
bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
+ _THREAD_SAFE_METHOD_
+
bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
lines_dirty = true;
return res;
}
void TextParagraph::set_alignment(HorizontalAlignment p_alignment) {
+ _THREAD_SAFE_METHOD_
+
if (alignment != p_alignment) {
if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
alignment = p_alignment;
@@ -394,11 +432,15 @@ HorizontalAlignment TextParagraph::get_alignment() const {
}
void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
+ _THREAD_SAFE_METHOD_
+
tab_stops = p_tab_stops;
lines_dirty = true;
}
void TextParagraph::set_flags(uint16_t p_flags) {
+ _THREAD_SAFE_METHOD_
+
if (flags != p_flags) {
flags = p_flags;
lines_dirty = true;
@@ -410,6 +452,8 @@ uint16_t TextParagraph::get_flags() const {
}
void TextParagraph::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) {
+ _THREAD_SAFE_METHOD_
+
if (overrun_behavior != p_behavior) {
overrun_behavior = p_behavior;
lines_dirty = true;
@@ -421,6 +465,8 @@ TextParagraph::OverrunBehavior TextParagraph::get_text_overrun_behavior() const
}
void TextParagraph::set_width(float p_width) {
+ _THREAD_SAFE_METHOD_
+
if (width != p_width) {
width = p_width;
lines_dirty = true;
@@ -432,6 +478,8 @@ float TextParagraph::get_width() const {
}
Size2 TextParagraph::get_non_wrapped_size() const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
@@ -441,9 +489,11 @@ Size2 TextParagraph::get_non_wrapped_size() const {
}
Size2 TextParagraph::get_size() const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
Size2 size;
- int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
for (int i = 0; i < visible_lines; i++) {
Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
@@ -458,11 +508,15 @@ Size2 TextParagraph::get_size() const {
}
int TextParagraph::get_line_count() const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- return lines_rid.size();
+ return (int)lines_rid.size();
}
void TextParagraph::set_max_lines_visible(int p_lines) {
+ _THREAD_SAFE_METHOD_
+
if (p_lines != max_lines_visible) {
max_lines_visible = p_lines;
lines_dirty = true;
@@ -474,14 +528,18 @@ int TextParagraph::get_max_lines_visible() const {
}
Array TextParagraph::get_line_objects(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Array());
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Array());
return TS->shaped_text_get_objects(lines_rid[p_line]);
}
Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Rect2());
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Rect2());
Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
for (int i = 0; i < p_line; i++) {
Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
@@ -495,8 +553,10 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
}
Size2 TextParagraph::get_line_size(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Size2());
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Size2());
if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y + spacing_top + spacing_bottom);
} else {
@@ -505,42 +565,56 @@ Size2 TextParagraph::get_line_size(int p_line) const {
}
Vector2i TextParagraph::get_line_range(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Vector2i());
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Vector2i());
return TS->shaped_text_get_range(lines_rid[p_line]);
}
float TextParagraph::get_line_ascent(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
return TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
}
float TextParagraph::get_line_descent(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
return TS->shaped_text_get_descent(lines_rid[p_line]) + spacing_bottom;
}
float TextParagraph::get_line_width(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
return TS->shaped_text_get_width(lines_rid[p_line]);
}
float TextParagraph::get_line_underline_position(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
return TS->shaped_text_get_underline_position(lines_rid[p_line]);
}
float TextParagraph::get_line_underline_thickness(int p_line) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
return TS->shaped_text_get_underline_thickness(lines_rid[p_line]);
}
Size2 TextParagraph::get_dropcap_size() const {
+ _THREAD_SAFE_METHOD_
+
return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position;
}
@@ -549,6 +623,8 @@ int TextParagraph::get_dropcap_lines() const {
}
void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
Vector2 ofs = p_pos;
float h_offset = 0.f;
@@ -571,7 +647,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color);
}
- int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
for (int i = 0; i < lines_visible; i++) {
float l_width = width;
@@ -650,6 +726,8 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
}
void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
Vector2 ofs = p_pos;
@@ -673,7 +751,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color);
}
- for (int i = 0; i < lines_rid.size(); i++) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
float l_width = width;
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
@@ -750,6 +828,8 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
}
int TextParagraph::hit_test(const Point2 &p_coords) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
Vector2 ofs;
if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
@@ -761,7 +841,7 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
return 0;
}
}
- for (int i = 0; i < lines_rid.size(); i++) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) {
return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x);
@@ -778,6 +858,8 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
}
void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+
Vector2 ofs = p_pos;
float h_offset = 0.f;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
@@ -800,6 +882,8 @@ void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color
}
void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+
Vector2 ofs = p_pos;
float h_offset = 0.f;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
@@ -822,8 +906,10 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int
}
void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size());
+ ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size());
Vector2 ofs = p_pos;
@@ -836,8 +922,10 @@ void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, co
}
void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const {
+ _THREAD_SAFE_METHOD_
+
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size());
+ ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size());
Vector2 ofs = p_pos;
if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
@@ -862,7 +950,7 @@ TextParagraph::TextParagraph() {
}
TextParagraph::~TextParagraph() {
- for (int i = 0; i < lines_rid.size(); i++) {
+ for (int i = 0; i < (int)lines_rid.size(); i++) {
TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 8a8a53943b..4f1aad16b5 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -31,6 +31,7 @@
#ifndef TEXT_PARAGRAPH_H
#define TEXT_PARAGRAPH_H
+#include "core/templates/local_vector.h"
#include "scene/resources/font.h"
#include "servers/text_server.h"
@@ -38,6 +39,7 @@
class TextParagraph : public RefCounted {
GDCLASS(TextParagraph, RefCounted);
+ _THREAD_SAFE_CLASS_
public:
enum OverrunBehavior {
@@ -54,7 +56,7 @@ private:
Rect2 dropcap_margins;
RID rid;
- Vector<RID> lines_rid;
+ LocalVector<RID> lines_rid;
int spacing_top = 0;
int spacing_bottom = 0;
@@ -156,6 +158,8 @@ public:
int hit_test(const Point2 &p_coords) const;
+ Mutex &get_mutex() const { return _thread_safe_; };
+
TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
TextParagraph();
~TextParagraph();