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.cpp90
1 files changed, 77 insertions, 13 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 94c2a9e64b..cf7bf930a5 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -414,7 +414,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font
}
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"));
+ return _calculate_line_vertical_offset(l);
}
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) {
@@ -683,7 +683,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
*r_char_offset = l.char_offset + l.char_count;
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"));
+ return _calculate_line_vertical_offset(l);
}
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) {
@@ -1566,7 +1566,7 @@ int RichTextLabel::_find_first_line(int p_from, int p_to, int p_vofs) const {
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"));
+ int ofs = _calculate_line_vertical_offset(main->lines[m]);
if (ofs < p_vofs) {
l = m + 1;
} else {
@@ -1576,6 +1576,10 @@ int RichTextLabel::_find_first_line(int p_from, int p_to, int p_vofs) const {
return l;
}
+_FORCE_INLINE_ float RichTextLabel::_calculate_line_vertical_offset(const RichTextLabel::Line &line) const {
+ return line.get_height(get_theme_constant(SNAME("line_separation")));
+}
+
void RichTextLabel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_MOUSE_EXIT: {
@@ -1609,6 +1613,7 @@ void RichTextLabel::_notification(int p_what) {
update();
} break;
+ case NOTIFICATION_PREDELETE:
case NOTIFICATION_EXIT_TREE: {
_stop_thread();
} break;
@@ -2489,7 +2494,7 @@ bool RichTextLabel::_validate_line_caches() {
// Resize lines without reshaping.
int fi = main->first_resized_line.load();
- float total_height = 0;
+ float total_height = (fi == 0) ? 0 : _calculate_line_vertical_offset(main->lines[fi - 1]);
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);
@@ -2551,6 +2556,10 @@ bool RichTextLabel::_validate_line_caches() {
void RichTextLabel::_process_line_caches() {
// Shape invalid lines.
+ if (!is_inside_tree()) {
+ return;
+ }
+
MutexLock data_lock(data_mutex);
Rect2 text_rect = _get_text_rect();
@@ -2560,10 +2569,9 @@ void RichTextLabel::_process_line_caches() {
int fi = main->first_invalid_line.load();
int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count);
- float total_height = 0;
+ float total_height = (fi == 0) ? 0 : _calculate_line_vertical_offset(main->lines[fi - 1]);
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);
-
updating_scroll = true;
bool exceeds = total_height > ctrl_height && scroll_active;
if (exceeds != scroll_visible) {
@@ -2577,11 +2585,11 @@ void RichTextLabel::_process_line_caches() {
scroll_w = 0;
vscroll->hide();
}
-
main->first_invalid_line.store(0);
main->first_resized_line.store(0);
main->first_invalid_font_line.store(0);
+ // since scroll was added or removed we need to resize all lines
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);
@@ -3369,21 +3377,34 @@ void RichTextLabel::append_text(const String &p_bbcode) {
bool in_bold = false;
bool in_italics = false;
+ bool after_list_open_tag = false;
+ bool after_list_close_tag = false;
set_process_internal(false);
- while (pos < p_bbcode.length()) {
+ while (pos <= p_bbcode.length()) {
int brk_pos = p_bbcode.find("[", pos);
if (brk_pos < 0) {
brk_pos = p_bbcode.length();
}
- if (brk_pos > pos) {
- add_text(p_bbcode.substr(pos, brk_pos - pos));
+ String text = brk_pos > pos ? p_bbcode.substr(pos, brk_pos - pos) : "";
+
+ // Trim the first newline character, it may be added later as needed.
+ if (after_list_close_tag || after_list_open_tag) {
+ text = text.trim_prefix("\n");
}
if (brk_pos == p_bbcode.length()) {
+ // For tags that are not properly closed.
+ if (text.is_empty() && after_list_open_tag) {
+ text = "\n";
+ }
+
+ if (!text.is_empty()) {
+ add_text(text);
+ }
break; //nothing else to add
}
@@ -3391,7 +3412,8 @@ void RichTextLabel::append_text(const String &p_bbcode) {
if (brk_end == -1) {
//no close, add the rest
- add_text(p_bbcode.substr(brk_pos, p_bbcode.length() - brk_pos));
+ text += p_bbcode.substr(brk_pos, p_bbcode.length() - brk_pos);
+ add_text(text);
break;
}
@@ -3437,18 +3459,60 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
if (!tag_ok) {
- add_text("[" + tag);
+ text += "[" + tag;
+ add_text(text);
+ after_list_open_tag = false;
+ after_list_close_tag = false;
pos = brk_end;
continue;
}
+ if (text.is_empty() && after_list_open_tag) {
+ text = "\n"; // Make empty list have at least one item.
+ }
+ after_list_open_tag = false;
+
+ if (tag == "/ol" || tag == "/ul") {
+ if (!text.is_empty()) {
+ // Make sure text ends with a newline character, that is, the last item
+ // will wrap at the end of block.
+ if (!text.ends_with("\n")) {
+ text += "\n";
+ }
+ } else if (!after_list_close_tag) {
+ text = "\n"; // Make the innermost list item wrap at the end of lists.
+ }
+ after_list_close_tag = true;
+ } else {
+ after_list_close_tag = false;
+ }
+
+ if (!text.is_empty()) {
+ add_text(text);
+ }
+
tag_stack.pop_front();
pos = brk_end + 1;
if (tag != "/img" && tag != "/dropcap") {
pop();
}
+ continue;
+ }
+
+ if (tag == "ol" || tag.begins_with("ol ") || tag == "ul" || tag.begins_with("ul ")) {
+ if (text.is_empty() && after_list_open_tag) {
+ text = "\n"; // Make each list have at least one item at the beginning.
+ }
+ after_list_open_tag = true;
+ } else {
+ after_list_open_tag = false;
+ }
+ if (!text.is_empty()) {
+ add_text(text);
+ }
+ after_list_close_tag = false;
- } else if (tag == "b") {
+ if (tag == "b") {
//use bold font
in_bold = true;
if (in_italics) {