summaryrefslogtreecommitdiff
path: root/scene/gui/rich_text_label.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/rich_text_label.cpp')
-rw-r--r--scene/gui/rich_text_label.cpp175
1 files changed, 134 insertions, 41 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index ba8727f9a0..3c45b90612 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -965,17 +965,18 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
uint32_t gl = glyphs[i].index;
uint16_t gl_fl = glyphs[i].flags;
uint8_t gl_cn = glyphs[i].count;
- bool cprev = false;
+ bool cprev_cluster = false;
+ bool cprev_conn = false;
if (gl_cn == 0) { // Parts of the same cluster, always connected.
- cprev = true;
+ cprev_cluster = true;
}
if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
- cprev = true;
+ cprev_conn = true;
}
} else {
if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
- cprev = true;
+ cprev_conn = true;
}
}
@@ -994,6 +995,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
+ bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
+
if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
@@ -1024,7 +1027,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_SHAKE) {
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
- if (!cprev) {
+ if (!cn) {
uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
uint64_t max_rand = 2147483647;
@@ -1038,7 +1041,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_WAVE) {
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
- if (!cprev) {
+ if (!cn) {
double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f);
item_wave->prev_off = Point2(0, 1) * value;
}
@@ -1046,7 +1049,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_TORNADO) {
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
- if (!cprev) {
+ if (!cn) {
double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
item_tornado->prev_off = Point2(torn_x, torn_y);
@@ -1181,17 +1184,18 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
uint32_t gl = glyphs[i].index;
uint16_t gl_fl = glyphs[i].flags;
uint8_t gl_cn = glyphs[i].count;
- bool cprev = false;
+ bool cprev_cluster = false;
+ bool cprev_conn = false;
if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
- cprev = true;
+ cprev_cluster = true;
}
if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
- cprev = true;
+ cprev_conn = true;
}
} else {
if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
- cprev = true;
+ cprev_conn = true;
}
}
@@ -1209,6 +1213,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
+ bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
+
if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
@@ -1239,7 +1245,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_SHAKE) {
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
- if (!cprev) {
+ if (!cn) {
uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
uint64_t max_rand = 2147483647;
@@ -1253,7 +1259,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_WAVE) {
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
- if (!cprev) {
+ if (!cn) {
double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
item_wave->prev_off = Point2(0, 1) * value;
}
@@ -1261,7 +1267,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_TORNADO) {
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
- if (!cprev) {
+ if (!cn) {
double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
item_tornado->prev_off = Point2(torn_x, torn_y);
@@ -1664,7 +1670,7 @@ int RichTextLabel::_find_first_line(int p_from, int p_to, int p_vofs) const {
r = m;
}
}
- return l;
+ return MIN(l, (int)main->lines.size() - 1);
}
_FORCE_INLINE_ float RichTextLabel::_calculate_line_vertical_offset(const RichTextLabel::Line &line) const {
@@ -1676,8 +1682,8 @@ void RichTextLabel::_update_theme_item_cache() {
theme_cache.normal_style = get_theme_stylebox(SNAME("normal"));
theme_cache.focus_style = get_theme_stylebox(SNAME("focus"));
- theme_cache.progress_bg_style = get_theme_stylebox(SNAME("bg"), SNAME("ProgressBar"));
- theme_cache.progress_fg_style = get_theme_stylebox(SNAME("fg"), SNAME("ProgressBar"));
+ theme_cache.progress_bg_style = get_theme_stylebox(SNAME("background"), SNAME("ProgressBar"));
+ theme_cache.progress_fg_style = get_theme_stylebox(SNAME("fill"), SNAME("ProgressBar"));
theme_cache.line_separation = get_theme_constant(SNAME("line_separation"));
@@ -1693,7 +1699,6 @@ void RichTextLabel::_update_theme_item_cache() {
theme_cache.shadow_offset_x = get_theme_constant(SNAME("shadow_offset_x"));
theme_cache.shadow_offset_y = get_theme_constant(SNAME("shadow_offset_y"));
theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
- theme_cache.outline_color = get_theme_color(SNAME("outline_color"));
theme_cache.bold_font = get_theme_font(SNAME("bold_font"));
theme_cache.bold_font_size = get_theme_font_size(SNAME("bold_font_size"));
@@ -2189,6 +2194,30 @@ RichTextLabel::ItemFont *RichTextLabel::_find_font(Item *p_item) {
while (fontitem) {
if (fontitem->type == ITEM_FONT) {
ItemFont *fi = static_cast<ItemFont *>(fontitem);
+ switch (fi->def_font) {
+ case NORMAL_FONT: {
+ fi->font = theme_cache.normal_font;
+ fi->font_size = theme_cache.normal_font_size;
+ } break;
+ case BOLD_FONT: {
+ fi->font = theme_cache.bold_font;
+ fi->font_size = theme_cache.bold_font_size;
+ } break;
+ case ITALICS_FONT: {
+ fi->font = theme_cache.italics_font;
+ fi->font_size = theme_cache.italics_font_size;
+ } break;
+ case BOLD_ITALICS_FONT: {
+ fi->font = theme_cache.bold_italics_font;
+ fi->font_size = theme_cache.bold_italics_font_size;
+ } break;
+ case MONO_FONT: {
+ fi->font = theme_cache.mono_font;
+ fi->font_size = theme_cache.mono_font_size;
+ } break;
+ default: {
+ } break;
+ }
return fi;
}
@@ -2564,7 +2593,9 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
void RichTextLabel::_thread_function(void *self) {
RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self);
+ rtl->set_physics_process_internal(true);
rtl->_process_line_caches();
+ rtl->set_physics_process_internal(false);
rtl->updating.store(false);
rtl->call_deferred(SNAME("queue_redraw"));
}
@@ -2680,7 +2711,6 @@ bool RichTextLabel::_validate_line_caches() {
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();
@@ -3004,6 +3034,17 @@ void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font
_add_item(item, false);
}
+void RichTextLabel::_push_def_font(DefaultFont p_font) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemFont *item = memnew(ItemFont);
+
+ item->def_font = p_font;
+ _add_item(item, true);
+}
+
void RichTextLabel::push_font(const Ref<Font> &p_font, int p_size) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -3020,31 +3061,31 @@ void RichTextLabel::push_font(const Ref<Font> &p_font, int p_size) {
void RichTextLabel::push_normal() {
ERR_FAIL_COND(theme_cache.normal_font.is_null());
- push_font(theme_cache.normal_font, theme_cache.normal_font_size);
+ _push_def_font(NORMAL_FONT);
}
void RichTextLabel::push_bold() {
ERR_FAIL_COND(theme_cache.bold_font.is_null());
- push_font(theme_cache.bold_font, theme_cache.bold_font_size);
+ _push_def_font(BOLD_FONT);
}
void RichTextLabel::push_bold_italics() {
ERR_FAIL_COND(theme_cache.bold_italics_font.is_null());
- push_font(theme_cache.bold_italics_font, theme_cache.bold_italics_font_size);
+ _push_def_font(BOLD_ITALICS_FONT);
}
void RichTextLabel::push_italics() {
ERR_FAIL_COND(theme_cache.italics_font.is_null());
- push_font(theme_cache.italics_font, theme_cache.italics_font_size);
+ _push_def_font(ITALICS_FONT);
}
void RichTextLabel::push_mono() {
ERR_FAIL_COND(theme_cache.mono_font.is_null());
- push_font(theme_cache.mono_font, theme_cache.mono_font_size);
+ _push_def_font(MONO_FONT);
}
void RichTextLabel::push_font_size(int p_font_size) {
@@ -3201,33 +3242,36 @@ void RichTextLabel::push_fade(int p_start_index, int p_length) {
_add_item(item, true);
}
-void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
+void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f, bool p_connected = true) {
_stop_thread();
MutexLock data_lock(data_mutex);
ItemShake *item = memnew(ItemShake);
item->strength = p_strength;
item->rate = p_rate;
+ item->connected = p_connected;
_add_item(item, true);
}
-void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
+void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f, bool p_connected = true) {
_stop_thread();
MutexLock data_lock(data_mutex);
ItemWave *item = memnew(ItemWave);
item->frequency = p_frequency;
item->amplitude = p_amplitude;
+ item->connected = p_connected;
_add_item(item, true);
}
-void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
+void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f, bool p_connected = true) {
_stop_thread();
MutexLock data_lock(data_mutex);
ItemTornado *item = memnew(ItemTornado);
item->frequency = p_frequency;
item->radius = p_radius;
+ item->connected = p_connected;
_add_item(item, true);
}
@@ -3635,9 +3679,9 @@ void RichTextLabel::append_text(const String &p_bbcode) {
//use bold font
in_bold = true;
if (in_italics) {
- push_font(theme_cache.bold_italics_font, theme_cache.bold_italics_font_size);
+ _push_def_font(BOLD_ITALICS_FONT);
} else {
- push_font(theme_cache.bold_font, theme_cache.bold_font_size);
+ _push_def_font(BOLD_FONT);
}
pos = brk_end + 1;
tag_stack.push_front(tag);
@@ -3645,15 +3689,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
//use italics font
in_italics = true;
if (in_bold) {
- push_font(theme_cache.bold_italics_font, theme_cache.bold_italics_font_size);
+ _push_def_font(BOLD_ITALICS_FONT);
} else {
- push_font(theme_cache.italics_font, theme_cache.italics_font_size);
+ _push_def_font(ITALICS_FONT);
}
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code") {
//use monospace font
- push_font(theme_cache.mono_font, theme_cache.mono_font_size);
+ _push_def_font(MONO_FONT);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("table=")) {
@@ -3941,7 +3985,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
int fs = theme_cache.normal_font_size * 3;
Ref<Font> f = theme_cache.normal_font;
Color color = theme_cache.default_color;
- Color outline_color = theme_cache.outline_color;
+ Color outline_color = theme_cache.font_outline_color;
int outline_size = theme_cache.outline_size;
Rect2 dropcap_margins = Rect2();
@@ -4230,7 +4274,13 @@ void RichTextLabel::append_text(const String &p_bbcode) {
rate = rate_option->value.to_float();
}
- push_shake(strength, rate);
+ bool connected = true;
+ OptionMap::Iterator connected_option = bbcode_options.find("connected");
+ if (connected_option) {
+ connected = connected_option->value.to_int();
+ }
+
+ push_shake(strength, rate, connected);
pos = brk_end + 1;
tag_stack.push_front("shake");
set_process_internal(true);
@@ -4247,7 +4297,13 @@ void RichTextLabel::append_text(const String &p_bbcode) {
period = period_option->value.to_float();
}
- push_wave(period, amplitude);
+ bool connected = true;
+ OptionMap::Iterator connected_option = bbcode_options.find("connected");
+ if (connected_option) {
+ connected = connected_option->value.to_int();
+ }
+
+ push_wave(period, amplitude, connected);
pos = brk_end + 1;
tag_stack.push_front("wave");
set_process_internal(true);
@@ -4264,7 +4320,13 @@ void RichTextLabel::append_text(const String &p_bbcode) {
frequency = frequency_option->value.to_float();
}
- push_tornado(frequency, radius);
+ bool connected = true;
+ OptionMap::Iterator connected_option = bbcode_options.find("connected");
+ if (connected_option) {
+ connected = connected_option->value.to_int();
+ }
+
+ push_tornado(frequency, radius, connected);
pos = brk_end + 1;
tag_stack.push_front("tornado");
set_process_internal(true);
@@ -4364,16 +4426,18 @@ int RichTextLabel::get_visible_paragraph_count() const {
if (!is_visible()) {
return 0;
}
+
+ const_cast<RichTextLabel *>(this)->_validate_line_caches();
return visible_paragraph_count;
}
void RichTextLabel::scroll_to_line(int p_line) {
- _validate_line_caches();
-
if (p_line <= 0) {
vscroll->set_value(0);
return;
}
+ _validate_line_caches();
+
int line_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
@@ -4392,6 +4456,8 @@ void RichTextLabel::scroll_to_line(int p_line) {
}
float RichTextLabel::get_line_offset(int p_line) {
+ _validate_line_caches();
+
int line_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
@@ -4409,6 +4475,8 @@ float RichTextLabel::get_line_offset(int p_line) {
}
float RichTextLabel::get_paragraph_offset(int p_paragraph) {
+ _validate_line_caches();
+
int to_line = main->first_invalid_line.load();
if (0 <= p_paragraph && p_paragraph < to_line) {
return main->lines[p_paragraph].offset.y;
@@ -4417,6 +4485,8 @@ float RichTextLabel::get_paragraph_offset(int p_paragraph) {
}
int RichTextLabel::get_line_count() const {
+ const_cast<RichTextLabel *>(this)->_validate_line_caches();
+
int line_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
@@ -4430,6 +4500,8 @@ int RichTextLabel::get_visible_line_count() const {
if (!is_visible()) {
return 0;
}
+ const_cast<RichTextLabel *>(this)->_validate_line_caches();
+
return visible_line_count;
}
@@ -4631,7 +4703,10 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
queue_redraw();
return true;
}
- p_search_previous ? current_line-- : current_line++;
+
+ if (current_line != ending_line) {
+ p_search_previous ? current_line-- : current_line++;
+ }
}
if (p_from_selection && selection.active) {
@@ -4844,7 +4919,14 @@ void RichTextLabel::set_use_bbcode(bool p_enable) {
}
use_bbcode = p_enable;
notify_property_list_changed();
- set_text(text);
+
+ const String current_text = text;
+ if (use_bbcode) {
+ parse_bbcode(current_text);
+ } else { // raw text
+ clear();
+ add_text(current_text);
+ }
}
bool RichTextLabel::is_using_bbcode() const {
@@ -5005,7 +5087,12 @@ int RichTextLabel::get_content_height() const {
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() * theme_cache.line_separation;
+ if (theme_cache.line_separation < 0) {
+ // Do not apply to the last line to avoid cutting text.
+ 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() - 1) * theme_cache.line_separation;
+ } else {
+ 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() * theme_cache.line_separation;
+ }
}
return total_height;
}
@@ -5298,6 +5385,8 @@ int RichTextLabel::get_visible_characters() const {
}
int RichTextLabel::get_character_line(int p_char) {
+ _validate_line_caches();
+
int line_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
@@ -5318,6 +5407,8 @@ int RichTextLabel::get_character_line(int p_char) {
}
int RichTextLabel::get_character_paragraph(int p_char) {
+ _validate_line_caches();
+
int para_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
@@ -5349,6 +5440,8 @@ int RichTextLabel::get_total_character_count() const {
}
int RichTextLabel::get_total_glyph_count() const {
+ const_cast<RichTextLabel *>(this)->_validate_line_caches();
+
int tg = 0;
Item *it = main;
while (it) {