mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 07:53:26 +00:00
Merge pull request #111258 from Koyper/fix_rich_text_label_bullet_list_issues
[RichTextLabel] Fix bullet list font color and formatting issues
This commit is contained in:
commit
ccccb9d3cb
2 changed files with 145 additions and 98 deletions
|
@ -266,6 +266,110 @@ String RichTextLabel::_get_prefix(Item *p_item, const Vector<int> &p_list_index,
|
|||
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) {
|
||||
ERR_FAIL_NULL(p_frame);
|
||||
ERR_FAIL_COND(p_line < 0 || p_line >= (int)p_frame->lines.size());
|
||||
|
@ -273,50 +377,8 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
|
|||
Line &l = p_frame->lines[p_line];
|
||||
MutexLock lock(l.text_buf->get_mutex());
|
||||
|
||||
// Prefix.
|
||||
Vector<int> list_index;
|
||||
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;
|
||||
}
|
||||
// List.
|
||||
_add_list_prefixes(p_frame, p_line, l);
|
||||
|
||||
RID t = l.text_buf->get_rid();
|
||||
int spans = TS->shaped_get_span_count(t);
|
||||
|
@ -471,50 +533,8 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
|
|||
l.char_offset = *r_char_offset;
|
||||
l.char_count = 0;
|
||||
|
||||
// List prefix.
|
||||
Vector<int> list_index;
|
||||
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;
|
||||
}
|
||||
// List.
|
||||
_add_list_prefixes(p_frame, p_line, l);
|
||||
|
||||
// Add indent.
|
||||
l.indent = _find_margin(l.from, p_base_font, p_base_font_size) + l.prefix_width;
|
||||
|
@ -934,9 +954,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));
|
||||
if (l.text_prefix.is_valid() && line == 0 && !skip_prefix) {
|
||||
Color font_color = _find_color(l.from, p_base_color);
|
||||
int outline_size = _find_outline_size(l.from, p_outline_size);
|
||||
Color font_outline_color = _find_outline_color(l.from, p_outline_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 = l.prefix_outline_size == -1 ? _find_outline_size(l.from, p_outline_size) : l.prefix_outline_size;
|
||||
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);
|
||||
if (rtl) {
|
||||
if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) {
|
||||
|
@ -3951,13 +3971,13 @@ void RichTextLabel::add_text(const String &p_text) {
|
|||
}
|
||||
|
||||
if (eol) {
|
||||
ItemNewline *item = memnew(ItemNewline);
|
||||
ItemNewline *item = memnew(ItemNewline); // Sets item->type to ITEM_NEWLINE.
|
||||
item->owner = get_instance_id();
|
||||
item->rid = items.make_rid(item);
|
||||
item->line = current_frame->lines.size();
|
||||
_add_item(item, false);
|
||||
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;
|
||||
}
|
||||
_invalidate_current_line(current_frame);
|
||||
|
|
|
@ -157,10 +157,13 @@ protected:
|
|||
private:
|
||||
struct Item;
|
||||
|
||||
struct Line {
|
||||
Item *from = nullptr;
|
||||
struct Line { // Line is a paragraph.
|
||||
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;
|
||||
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;
|
||||
Ref<TextParagraph> text_buf;
|
||||
|
||||
|
@ -196,17 +199,17 @@ private:
|
|||
struct Item {
|
||||
int index = 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;
|
||||
List<Item *> subitems;
|
||||
List<Item *>::Element *E = nullptr;
|
||||
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 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);
|
||||
while (subitems.size()) {
|
||||
Item *subitem = subitems.front()->get();
|
||||
|
@ -507,6 +510,29 @@ private:
|
|||
struct ItemContext : public Item {
|
||||
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;
|
||||
Item *current = nullptr;
|
||||
|
@ -711,6 +737,7 @@ private:
|
|||
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);
|
||||
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 Vector<String> _split_unquoted(const String &p_src, char32_t p_splitter);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue