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.cpp278
1 files changed, 190 insertions, 88 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 05824d54f1..984f20ee58 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -149,7 +149,12 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item
return it;
}
} break;
- case ITEM_NEWLINE:
+ case ITEM_NEWLINE: {
+ offset += 1;
+ if (offset == p_position) {
+ return it;
+ }
+ } break;
case ITEM_IMAGE:
case ITEM_TABLE: {
offset += 1;
@@ -219,13 +224,21 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
for (int i = 0; i < spans; i++) {
ItemText *it = reinterpret_cast<ItemText *>((uint64_t)TS->shaped_get_span_meta(t, i));
if (it) {
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(it);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
for (int j = 0; j < TextServer::SPACING_MAX; j++) {
@@ -426,10 +439,10 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Line &l = p_frame->lines[p_line];
MutexLock lock(l.text_buf->get_mutex());
- uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
+ BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
switch (autowrap_mode) {
case TextServer::AUTOWRAP_WORD_SMART:
- autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;
break;
case TextServer::AUTOWRAP_WORD:
autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
@@ -443,7 +456,8 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Clear cache.
l.text_buf->clear();
- l.text_buf->set_flags(autowrap_flags | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
+ l.text_buf->set_break_flags(autowrap_flags);
+ l.text_buf->set_justification_flags(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
l.char_offset = *r_char_offset;
l.char_count = 0;
@@ -477,13 +491,21 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
l.dc_ol_color = dc->ol_color;
} break;
case ITEM_NEWLINE: {
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(it);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
l.text_buf->add_string("\n", font, font_size);
text += "\n";
@@ -492,13 +514,21 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
} break;
case ITEM_TEXT: {
ItemText *t = static_cast<ItemText *>(it);
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(it);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
String lang = _find_language(it);
String tx = t->text;
@@ -744,13 +774,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
if (!prefix.is_empty()) {
- Ref<Font> font = _find_font(l.from);
- if (font.is_null()) {
- font = get_theme_font(SNAME("normal_font"));
+ Ref<Font> font = get_theme_font(SNAME("normal_font"));
+ int font_size = get_theme_font_size(SNAME("normal_font_size"));
+
+ ItemFont *font_it = _find_font(l.from);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(l.from);
- if (font_size == -1) {
- font_size = get_theme_font_size(SNAME("normal_font_size"));
+ ItemFontSize *font_size_it = _find_font_size(l.from);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
if (rtl) {
float offx = 0.0f;
@@ -783,17 +821,18 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
off.y += line_spacing;
}
- RID rid = l.text_buf->get_line_rid(line);
if (p_ofs.y + off.y >= ctrl_size.height) {
break;
}
- if (p_ofs.y + off.y + TS->shaped_text_get_size(rid).y <= 0) {
- off.y += TS->shaped_text_get_size(rid).y;
+
+ const Size2 line_size = l.text_buf->get_line_size(line);
+ if (p_ofs.y + off.y + line_size.y <= 0) {
+ off.y += line_size.y;
continue;
}
float width = l.text_buf->get_width();
- float length = TS->shaped_text_get_width(rid);
+ float length = line_size.x;
// Draw line.
line_count++;
@@ -836,6 +875,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
+ RID rid = l.text_buf->get_line_rid(line);
//draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
off.y += TS->shaped_text_get_ascent(rid);
@@ -1344,6 +1384,8 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
float RichTextLabel::_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, int *r_click_line, Item **r_click_item, int *r_click_char, bool p_table) {
Vector2 off;
+ bool line_clicked = false;
+ float text_rect_begin = 0.0;
int char_pos = -1;
Line &l = p_frame->lines[p_line];
MutexLock lock(l.text_buf->get_mutex());
@@ -1469,7 +1511,11 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
- char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
+ if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) {
+ char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
+ }
+ line_clicked = true;
+ text_rect_begin = rtl ? rect.position.x + rect.size.x : rect.position.x;
}
// If table hit was detected, and line hit is in the table bounds use table hit.
@@ -1496,23 +1542,45 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
// Text line hit.
- if (char_pos >= 0) {
+ if (line_clicked) {
// Find item.
if (r_click_item != nullptr) {
Item *it = p_frame->lines[p_line].from;
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) {
- *r_click_item = _get_prev_item(it_to);
- } else {
- for (Item *i = it; i; i = _get_next_item(i)) {
- *r_click_item = i;
+ if (char_pos >= 0) {
+ *r_click_item = _get_item_at_pos(it, it_to, char_pos);
+ } else {
+ int stop = text_rect_begin;
+ *r_click_item = _find_indentable(it);
+ while (*r_click_item) {
+ Ref<Font> font = get_theme_font(SNAME("normal_font"));
+ int font_size = get_theme_font_size(SNAME("normal_font_size"));
+ ItemFont *font_it = _find_font(*r_click_item);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
+ ItemFontSize *font_size_it = _find_font_size(*r_click_item);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
+ }
+ if (rtl) {
+ stop += tab_size * font->get_char_size(' ', font_size).width;
+ if (stop > p_click.x) {
+ break;
+ }
+ } else {
+ stop -= tab_size * font->get_char_size(' ', font_size).width;
+ if (stop < p_click.x) {
+ break;
+ }
+ }
+ *r_click_item = _find_indentable((*r_click_item)->parent);
}
- } else {
- // Selection in the line.
- *r_click_item = _get_item_at_pos(it, it_to, char_pos);
}
}
@@ -2069,34 +2137,47 @@ void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line)
}
}
-Ref<Font> RichTextLabel::_find_font(Item *p_item) {
+RichTextLabel::Item *RichTextLabel::_find_indentable(Item *p_item) {
+ Item *indentable = p_item;
+
+ while (indentable) {
+ if (indentable->type == ITEM_INDENT || indentable->type == ITEM_LIST) {
+ return indentable;
+ }
+ indentable = indentable->parent;
+ }
+
+ return indentable;
+}
+
+RichTextLabel::ItemFont *RichTextLabel::_find_font(Item *p_item) {
Item *fontitem = p_item;
while (fontitem) {
if (fontitem->type == ITEM_FONT) {
ItemFont *fi = static_cast<ItemFont *>(fontitem);
- return fi->font;
+ return fi;
}
fontitem = fontitem->parent;
}
- return Ref<Font>();
+ return nullptr;
}
-int RichTextLabel::_find_font_size(Item *p_item) {
+RichTextLabel::ItemFontSize *RichTextLabel::_find_font_size(Item *p_item) {
Item *sizeitem = p_item;
while (sizeitem) {
if (sizeitem->type == ITEM_FONT_SIZE) {
ItemFontSize *fi = static_cast<ItemFontSize *>(sizeitem);
- return fi->font_size;
+ return fi;
}
sizeitem = sizeitem->parent;
}
- return -1;
+ return nullptr;
}
int RichTextLabel::_find_outline_size(Item *p_item, int p_default) {
@@ -2182,24 +2263,40 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
while (item) {
if (item->type == ITEM_INDENT) {
- Ref<Font> font = _find_font(item);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(item);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(item);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(item);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
margin += tab_size * font->get_char_size(' ', font_size).width;
} else if (item->type == ITEM_LIST) {
- Ref<Font> font = _find_font(item);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(item);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(item);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(item);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
margin += tab_size * font->get_char_size(' ', font_size).width;
}
@@ -2878,7 +2975,7 @@ void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font
_add_item(item, false);
}
-void RichTextLabel::push_font(const Ref<Font> &p_font) {
+void RichTextLabel::push_font(const Ref<Font> &p_font, int p_size) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -2887,6 +2984,7 @@ void RichTextLabel::push_font(const Ref<Font> &p_font) {
ItemFont *item = memnew(ItemFont);
item->font = p_font;
+ item->font_size = p_size;
_add_item(item, true);
}
@@ -2894,35 +2992,35 @@ void RichTextLabel::push_normal() {
Ref<Font> normal_font = get_theme_font(SNAME("normal_font"));
ERR_FAIL_COND(normal_font.is_null());
- push_font(normal_font);
+ push_font(normal_font, get_theme_font_size(SNAME("normal_font_size")));
}
void RichTextLabel::push_bold() {
Ref<Font> bold_font = get_theme_font(SNAME("bold_font"));
ERR_FAIL_COND(bold_font.is_null());
- push_font(bold_font);
+ push_font(bold_font, get_theme_font_size(SNAME("bold_font_size")));
}
void RichTextLabel::push_bold_italics() {
Ref<Font> bold_italics_font = get_theme_font(SNAME("bold_italics_font"));
ERR_FAIL_COND(bold_italics_font.is_null());
- push_font(bold_italics_font);
+ push_font(bold_italics_font, get_theme_font_size(SNAME("bold_italics_font_size")));
}
void RichTextLabel::push_italics() {
Ref<Font> italics_font = get_theme_font(SNAME("italics_font"));
ERR_FAIL_COND(italics_font.is_null());
- push_font(italics_font);
+ push_font(italics_font, get_theme_font_size(SNAME("italics_font_size")));
}
void RichTextLabel::push_mono() {
Ref<Font> mono_font = get_theme_font(SNAME("mono_font"));
ERR_FAIL_COND(mono_font.is_null());
- push_font(mono_font);
+ push_font(mono_font, get_theme_font_size(SNAME("mono_font_size")));
}
void RichTextLabel::push_font_size(int p_font_size) {
@@ -3512,9 +3610,9 @@ void RichTextLabel::append_text(const String &p_bbcode) {
//use bold font
in_bold = true;
if (in_italics) {
- push_font(bold_italics_font);
+ push_font(bold_italics_font, get_theme_font_size(SNAME("bold_italics_font_size")));
} else {
- push_font(bold_font);
+ push_font(bold_font, get_theme_font_size(SNAME("bold_font_size")));
}
pos = brk_end + 1;
tag_stack.push_front(tag);
@@ -3522,15 +3620,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
//use italics font
in_italics = true;
if (in_bold) {
- push_font(bold_italics_font);
+ push_font(bold_italics_font, get_theme_font_size(SNAME("bold_italics_font_size")));
} else {
- push_font(italics_font);
+ push_font(italics_font, get_theme_font_size(SNAME("italics_font_size")));
}
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code") {
//use monospace font
- push_font(mono_font);
+ push_font(mono_font, get_theme_font_size(SNAME("mono_font_size")));
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("table=")) {
@@ -3959,9 +4057,16 @@ void RichTextLabel::append_text(const String &p_bbcode) {
String fnt_ftr = tag.substr(18, tag.length());
Vector<String> subtag = fnt_ftr.split(",");
if (subtag.size() > 0) {
- Ref<Font> font = _find_font(current);
- if (font.is_null()) {
- font = normal_font;
+ Ref<Font> font = normal_font;
+ int font_size = 0;
+ ItemFont *font_it = _find_font(current);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
Ref<FontVariation> fc;
fc.instantiate();
@@ -3976,7 +4081,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
fc->set_opentype_features(features);
- push_font(fc);
+ push_font(fc, font_size);
}
pos = brk_end + 1;
tag_stack.push_front("opentype_features");
@@ -3997,6 +4102,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
Ref<FontVariation> fc;
fc.instantiate();
+ int fnt_size = 0;
for (int i = 1; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=", true, 2);
if (subtag_a.size() == 2) {
@@ -4007,10 +4113,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
fc->set_base_font(font_data);
}
} else if (subtag_a[0] == "size" || subtag_a[0] == "s") {
- int fnt_size = subtag_a[1].to_int();
- if (fnt_size > 0) {
- push_font_size(fnt_size);
- }
+ fnt_size = subtag_a[1].to_int();
} else if (subtag_a[0] == "glyph_spacing" || subtag_a[0] == "gl") {
int spacing = subtag_a[1].to_int();
fc->set_spacing(TextServer::SPACING_GLYPH, spacing);
@@ -4061,7 +4164,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
}
- push_font(fc);
+ push_font(fc, fnt_size);
pos = brk_end + 1;
tag_stack.push_front("font");
@@ -4843,11 +4946,10 @@ void RichTextLabel::install_effect(const Variant effect) {
Ref<RichTextEffect> rteffect;
rteffect = effect;
- if (rteffect.is_valid()) {
- custom_effects.push_back(effect);
- if ((!text.is_empty()) && use_bbcode) {
- parse_bbcode(text);
- }
+ ERR_FAIL_COND_MSG(rteffect.is_null(), "Invalid RichTextEffect resource.");
+ custom_effects.push_back(effect);
+ if ((!text.is_empty()) && use_bbcode) {
+ parse_bbcode(text);
}
}
@@ -4890,7 +4992,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
- ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font);
ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size);
ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);