mirror of
https://github.com/godotengine/godot.git
synced 2025-12-08 06:09:55 +00:00
Fix RichTextLabel bullet list font issues
This commit is contained in:
parent
987832be46
commit
faf1ab126d
2 changed files with 145 additions and 98 deletions
|
|
@ -257,6 +257,110 @@ String RichTextLabel::_get_prefix(Item *p_item, const Vector<int> &p_list_index,
|
||||||
return prefix + " ";
|
return prefix + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RichTextLabel::_add_list_prefixes(ItemFrame *p_frame, int p_line, Line &r_l) {
|
||||||
|
Vector<int> list_index;
|
||||||
|
Vector<int> list_count;
|
||||||
|
Vector<ItemList *> list_items;
|
||||||
|
_find_list(r_l.from, list_index, list_count, list_items);
|
||||||
|
if (list_items.size() > 0) {
|
||||||
|
ItemList *this_list = list_items[0];
|
||||||
|
if (list_index[0] == 1) {
|
||||||
|
// List level start, shape all prefixes for this level and compute max. prefix width.
|
||||||
|
list_items[0]->max_width = 0;
|
||||||
|
int index = 0;
|
||||||
|
for (int i = p_line; i < (int)p_frame->lines.size(); i++) { // For all the list rows in all lists in this frame.
|
||||||
|
Line &list_row_line = p_frame->lines[i];
|
||||||
|
if (_find_list_item(list_row_line.from) == this_list) { // Is a row inside this list.
|
||||||
|
index++;
|
||||||
|
Ref<Font> font = theme_cache.normal_font;
|
||||||
|
int font_size = theme_cache.normal_font_size;
|
||||||
|
int list_row_char_ofs = list_row_line.from->char_ofs;
|
||||||
|
int item_font_size = -1;
|
||||||
|
ItemFont *found_font_item = nullptr;
|
||||||
|
Vector<Item *> formatting_items_info;
|
||||||
|
ItemText *this_row_text_item = nullptr;
|
||||||
|
Item *it = _get_next_item(this_list);
|
||||||
|
while (it && (this_row_text_item != nullptr || it->char_ofs <= list_row_char_ofs)) { // Find the ItemText for this list row. There is only one per row or none.
|
||||||
|
if (it->type == ITEM_TEXT && it->char_ofs == list_row_char_ofs) {
|
||||||
|
ItemText *text_item = static_cast<ItemText *>(it);
|
||||||
|
this_row_text_item = text_item;
|
||||||
|
// `parent` is the enclosing item tag, if any, which itself can be further enclosed by another tag and so on,
|
||||||
|
// all of which will be applied to the text item. The `parent` is an interval predecessor, not a hierarchical parent.
|
||||||
|
Item *parent = text_item->parent;
|
||||||
|
while (parent && parent != main) {
|
||||||
|
// `formatting_items` is an Array of all ITEM types affecting glyph appearance, like ITEM_FONT, ITEM_COLOR, etc.
|
||||||
|
if (formatting_items.has(parent->type)) {
|
||||||
|
formatting_items_info.push_back(parent);
|
||||||
|
}
|
||||||
|
parent = parent->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = _get_next_item(it);
|
||||||
|
}
|
||||||
|
if (this_row_text_item == nullptr) { // If the row doesn't have any text yet.
|
||||||
|
it = _get_next_item(this_list);
|
||||||
|
// All format items at the same char location should be applied to the prefix.
|
||||||
|
// This won't add any earlier tags.
|
||||||
|
while (it && it->char_ofs <= list_row_char_ofs) {
|
||||||
|
if (formatting_items.has(it->type) && it->char_ofs == list_row_char_ofs) {
|
||||||
|
formatting_items_info.push_back(it);
|
||||||
|
}
|
||||||
|
it = _get_next_item(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Item *format_item : formatting_items_info) {
|
||||||
|
switch (format_item->type) {
|
||||||
|
case ITEM_FONT: {
|
||||||
|
ItemFont *font_item = static_cast<ItemFont *>(format_item);
|
||||||
|
if (font_item->def_font != RTL_CUSTOM_FONT) {
|
||||||
|
font_item = _find_font(format_item); // Sets `def_font` based on font type.
|
||||||
|
}
|
||||||
|
if (font_item->font.is_valid()) {
|
||||||
|
if (font_item->def_font == RTL_BOLD_ITALICS_FONT) { // Always set bold italic.
|
||||||
|
found_font_item = font_item;
|
||||||
|
} else if (found_font_item == nullptr || found_font_item->def_font != RTL_BOLD_ITALICS_FONT) { // Don't overwrite BOLD_ITALIC with BOLD or ITALIC.
|
||||||
|
found_font_item = font_item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found_font_item->font_size > 0) {
|
||||||
|
font_size = found_font_item->font_size;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case ITEM_FONT_SIZE: {
|
||||||
|
ItemFontSize *font_size_item = static_cast<ItemFontSize *>(format_item);
|
||||||
|
item_font_size = font_size_item->font_size;
|
||||||
|
} break;
|
||||||
|
case ITEM_COLOR: {
|
||||||
|
ItemColor *color_item = static_cast<ItemColor *>(format_item);
|
||||||
|
list_row_line.prefix_color = color_item->color;
|
||||||
|
} break;
|
||||||
|
case ITEM_OUTLINE_SIZE: {
|
||||||
|
ItemOutlineSize *outline_size_item = static_cast<ItemOutlineSize *>(format_item);
|
||||||
|
list_row_line.prefix_outline_size = outline_size_item->outline_size;
|
||||||
|
} break;
|
||||||
|
case ITEM_OUTLINE_COLOR: {
|
||||||
|
ItemOutlineColor *outline_color_item = static_cast<ItemOutlineColor *>(format_item);
|
||||||
|
list_row_line.prefix_outline_color = outline_color_item->color;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font = found_font_item != nullptr ? found_font_item->font : font;
|
||||||
|
font_size = item_font_size != -1 ? item_font_size : font_size;
|
||||||
|
list_index.write[0] = index;
|
||||||
|
String prefix = _get_prefix(list_row_line.from, list_index, list_items);
|
||||||
|
list_row_line.text_prefix.instantiate();
|
||||||
|
list_row_line.text_prefix->set_direction(_find_direction(list_row_line.from));
|
||||||
|
list_row_line.text_prefix->add_string(prefix, font, font_size);
|
||||||
|
list_items.write[0]->max_width = MAX(this_list->max_width, list_row_line.text_prefix->get_size().x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r_l.prefix_width = this_list->max_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) {
|
void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) {
|
||||||
ERR_FAIL_NULL(p_frame);
|
ERR_FAIL_NULL(p_frame);
|
||||||
ERR_FAIL_COND(p_line < 0 || p_line >= (int)p_frame->lines.size());
|
ERR_FAIL_COND(p_line < 0 || p_line >= (int)p_frame->lines.size());
|
||||||
|
|
@ -264,50 +368,8 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
|
||||||
Line &l = p_frame->lines[p_line];
|
Line &l = p_frame->lines[p_line];
|
||||||
MutexLock lock(l.text_buf->get_mutex());
|
MutexLock lock(l.text_buf->get_mutex());
|
||||||
|
|
||||||
// Prefix.
|
// List.
|
||||||
Vector<int> list_index;
|
_add_list_prefixes(p_frame, p_line, l);
|
||||||
Vector<int> list_count;
|
|
||||||
Vector<ItemList *> list_items;
|
|
||||||
_find_list(l.from, list_index, list_count, list_items);
|
|
||||||
|
|
||||||
if (list_items.size() > 0) {
|
|
||||||
if (list_index[0] == 1) {
|
|
||||||
// List level start, shape all prefixes for this level and compute max. prefix width.
|
|
||||||
list_items[0]->max_width = 0;
|
|
||||||
int index = 0;
|
|
||||||
for (int i = p_line; i < (int)p_frame->lines.size(); i++) {
|
|
||||||
Line &list_l = p_frame->lines[i];
|
|
||||||
if (_find_list_item(list_l.from) == list_items[0]) {
|
|
||||||
index++;
|
|
||||||
|
|
||||||
Ref<Font> font = theme_cache.normal_font;
|
|
||||||
int font_size = theme_cache.normal_font_size;
|
|
||||||
|
|
||||||
ItemFont *font_it = _find_font(list_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ItemFontSize *font_size_it = _find_font_size(list_l.from);
|
|
||||||
if (font_size_it && font_size_it->font_size > 0) {
|
|
||||||
font_size = font_size_it->font_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_index.write[0] = index;
|
|
||||||
String prefix = _get_prefix(list_l.from, list_index, list_items);
|
|
||||||
list_l.text_prefix.instantiate();
|
|
||||||
list_l.text_prefix->set_direction(_find_direction(list_l.from));
|
|
||||||
list_l.text_prefix->add_string(prefix, font, font_size);
|
|
||||||
list_items.write[0]->max_width = MAX(list_items[0]->max_width, list_l.text_prefix->get_size().x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.prefix_width = list_items[0]->max_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
RID t = l.text_buf->get_rid();
|
RID t = l.text_buf->get_rid();
|
||||||
int spans = TS->shaped_get_span_count(t);
|
int spans = TS->shaped_get_span_count(t);
|
||||||
|
|
@ -458,50 +520,8 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
|
||||||
l.char_offset = *r_char_offset;
|
l.char_offset = *r_char_offset;
|
||||||
l.char_count = 0;
|
l.char_count = 0;
|
||||||
|
|
||||||
// List prefix.
|
// List.
|
||||||
Vector<int> list_index;
|
_add_list_prefixes(p_frame, p_line, l);
|
||||||
Vector<int> list_count;
|
|
||||||
Vector<ItemList *> list_items;
|
|
||||||
_find_list(l.from, list_index, list_count, list_items);
|
|
||||||
|
|
||||||
if (list_items.size() > 0) {
|
|
||||||
if (list_index[0] == 1) {
|
|
||||||
// List level start, shape all prefixes for this level and compute max. prefix width.
|
|
||||||
list_items[0]->max_width = 0;
|
|
||||||
int index = 0;
|
|
||||||
for (int i = p_line; i < (int)p_frame->lines.size(); i++) {
|
|
||||||
Line &list_l = p_frame->lines[i];
|
|
||||||
if (_find_list_item(list_l.from) == list_items[0]) {
|
|
||||||
index++;
|
|
||||||
|
|
||||||
Ref<Font> font = theme_cache.normal_font;
|
|
||||||
int font_size = theme_cache.normal_font_size;
|
|
||||||
|
|
||||||
ItemFont *font_it = _find_font(list_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ItemFontSize *font_size_it = _find_font_size(list_l.from);
|
|
||||||
if (font_size_it && font_size_it->font_size > 0) {
|
|
||||||
font_size = font_size_it->font_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_index.write[0] = index;
|
|
||||||
String prefix = _get_prefix(list_l.from, list_index, list_items);
|
|
||||||
list_l.text_prefix.instantiate();
|
|
||||||
list_l.text_prefix->set_direction(_find_direction(list_l.from));
|
|
||||||
list_l.text_prefix->add_string(prefix, font, font_size);
|
|
||||||
list_items.write[0]->max_width = MAX(list_items[0]->max_width, list_l.text_prefix->get_size().x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.prefix_width = list_items[0]->max_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add indent.
|
// Add indent.
|
||||||
l.indent = _find_margin(l.from, p_base_font, p_base_font_size) + l.prefix_width;
|
l.indent = _find_margin(l.from, p_base_font, p_base_font_size) + l.prefix_width;
|
||||||
|
|
@ -909,9 +929,9 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
|
||||||
|
|
||||||
bool skip_prefix = (trim_chars && l.char_offset > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs));
|
bool skip_prefix = (trim_chars && l.char_offset > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs));
|
||||||
if (l.text_prefix.is_valid() && line == 0 && !skip_prefix) {
|
if (l.text_prefix.is_valid() && line == 0 && !skip_prefix) {
|
||||||
Color font_color = _find_color(l.from, p_base_color);
|
Color font_color = l.prefix_color == Color(0, 0, 0, 0) ? _find_color(l.from, p_base_color) : l.prefix_color;
|
||||||
int outline_size = _find_outline_size(l.from, p_outline_size);
|
int outline_size = l.prefix_outline_size == -1 ? _find_outline_size(l.from, p_outline_size) : l.prefix_outline_size;
|
||||||
Color font_outline_color = _find_outline_color(l.from, p_outline_color);
|
Color font_outline_color = l.prefix_outline_color == Color(0, 0, 0, 0) ? _find_outline_color(l.from, p_base_color) : l.prefix_outline_color;
|
||||||
Color font_shadow_color = p_font_shadow_color * Color(1, 1, 1, font_color.a);
|
Color font_shadow_color = p_font_shadow_color * Color(1, 1, 1, font_color.a);
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) {
|
if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) {
|
||||||
|
|
@ -3866,13 +3886,13 @@ void RichTextLabel::add_text(const String &p_text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eol) {
|
if (eol) {
|
||||||
ItemNewline *item = memnew(ItemNewline);
|
ItemNewline *item = memnew(ItemNewline); // Sets item->type to ITEM_NEWLINE.
|
||||||
item->owner = get_instance_id();
|
item->owner = get_instance_id();
|
||||||
item->rid = items.make_rid(item);
|
item->rid = items.make_rid(item);
|
||||||
item->line = current_frame->lines.size();
|
item->line = current_frame->lines.size();
|
||||||
_add_item(item, false);
|
_add_item(item, false);
|
||||||
current_frame->lines.resize(current_frame->lines.size() + 1);
|
current_frame->lines.resize(current_frame->lines.size() + 1);
|
||||||
if (item->type != ITEM_NEWLINE) {
|
if (item->type != ITEM_NEWLINE) { // item IS an ITEM_NEWLINE so this will never get called?
|
||||||
current_frame->lines[current_frame->lines.size() - 1].from = item;
|
current_frame->lines[current_frame->lines.size() - 1].from = item;
|
||||||
}
|
}
|
||||||
_invalidate_current_line(current_frame);
|
_invalidate_current_line(current_frame);
|
||||||
|
|
|
||||||
|
|
@ -153,10 +153,13 @@ protected:
|
||||||
private:
|
private:
|
||||||
struct Item;
|
struct Item;
|
||||||
|
|
||||||
struct Line {
|
struct Line { // Line is a paragraph.
|
||||||
Item *from = nullptr;
|
Item *from = nullptr; // `from` is main if this Line is the first Line in the doc, otherwise `from` is the previous Item in the doc of any type.
|
||||||
|
|
||||||
Ref<TextLine> text_prefix;
|
Ref<TextLine> text_prefix;
|
||||||
|
Color prefix_color = Color(0, 0, 0, 0);
|
||||||
|
int prefix_outline_size = -1;
|
||||||
|
Color prefix_outline_color = Color(0, 0, 0, 0);
|
||||||
float prefix_width = 0;
|
float prefix_width = 0;
|
||||||
Ref<TextParagraph> text_buf;
|
Ref<TextParagraph> text_buf;
|
||||||
|
|
||||||
|
|
@ -192,17 +195,17 @@ private:
|
||||||
struct Item {
|
struct Item {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int char_ofs = 0;
|
int char_ofs = 0;
|
||||||
Item *parent = nullptr;
|
Item *parent = nullptr; // "parent" means "enclosing item tag", if any. It is an interval predecessor, not a hierarchical parent.
|
||||||
ItemType type = ITEM_FRAME;
|
ItemType type = ITEM_FRAME;
|
||||||
List<Item *> subitems;
|
List<Item *> subitems;
|
||||||
List<Item *>::Element *E = nullptr;
|
List<Item *>::Element *E = nullptr;
|
||||||
ObjectID owner;
|
ObjectID owner;
|
||||||
int line = 0;
|
int line = 0; // `line` is the index number of the paragraph (Line) this item is inside of (zero if the first paragraph).
|
||||||
RID rid;
|
RID rid;
|
||||||
|
|
||||||
RID accessibility_item_element;
|
RID accessibility_item_element;
|
||||||
|
|
||||||
void _clear_children() {
|
void _clear_children() { // Only ever called on main or a paragraph (Line).
|
||||||
RichTextLabel *owner_rtl = ObjectDB::get_instance<RichTextLabel>(owner);
|
RichTextLabel *owner_rtl = ObjectDB::get_instance<RichTextLabel>(owner);
|
||||||
while (subitems.size()) {
|
while (subitems.size()) {
|
||||||
Item *subitem = subitems.front()->get();
|
Item *subitem = subitems.front()->get();
|
||||||
|
|
@ -503,6 +506,29 @@ private:
|
||||||
struct ItemContext : public Item {
|
struct ItemContext : public Item {
|
||||||
ItemContext() { type = ITEM_CONTEXT; }
|
ItemContext() { type = ITEM_CONTEXT; }
|
||||||
};
|
};
|
||||||
|
const Array formatting_items = {
|
||||||
|
// all ITEM types affecting glyph appearance.
|
||||||
|
ITEM_FONT,
|
||||||
|
ITEM_FONT_SIZE,
|
||||||
|
ITEM_FONT_FEATURES,
|
||||||
|
ITEM_COLOR,
|
||||||
|
ITEM_OUTLINE_SIZE,
|
||||||
|
ITEM_OUTLINE_COLOR,
|
||||||
|
ITEM_UNDERLINE,
|
||||||
|
ITEM_STRIKETHROUGH,
|
||||||
|
ITEM_FADE,
|
||||||
|
ITEM_SHAKE,
|
||||||
|
ITEM_WAVE,
|
||||||
|
ITEM_TORNADO,
|
||||||
|
ITEM_RAINBOW,
|
||||||
|
ITEM_BGCOLOR,
|
||||||
|
ITEM_FGCOLOR,
|
||||||
|
ITEM_META,
|
||||||
|
ITEM_HINT,
|
||||||
|
ITEM_CUSTOMFX,
|
||||||
|
ITEM_LANGUAGE,
|
||||||
|
ITEM_PULSE,
|
||||||
|
};
|
||||||
|
|
||||||
ItemFrame *main = nullptr;
|
ItemFrame *main = nullptr;
|
||||||
Item *current = nullptr;
|
Item *current = nullptr;
|
||||||
|
|
@ -691,6 +717,7 @@ private:
|
||||||
Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2());
|
Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2());
|
||||||
|
|
||||||
String _get_prefix(Item *p_item, const Vector<int> &p_list_index, const Vector<ItemList *> &p_list_items);
|
String _get_prefix(Item *p_item, const Vector<int> &p_list_index, const Vector<ItemList *> &p_list_items);
|
||||||
|
void _add_list_prefixes(ItemFrame *p_frame, int p_line, Line &r_l);
|
||||||
|
|
||||||
static int _find_unquoted(const String &p_src, char32_t p_chr, int p_from);
|
static int _find_unquoted(const String &p_src, char32_t p_chr, int p_from);
|
||||||
static Vector<String> _split_unquoted(const String &p_src, char32_t p_splitter);
|
static Vector<String> _split_unquoted(const String &p_src, char32_t p_splitter);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue