Merge pull request #92605 from aaronp64/richtextlabel_table_overlap

Fix `RichTextLabel` table overlapping with next line
This commit is contained in:
Rémi Verschelde 2024-06-03 10:35:54 +02:00
commit 353f7bf6af
No known key found for this signature in database
GPG key ID: C3336907360768E1
2 changed files with 130 additions and 242 deletions

View file

@ -318,7 +318,6 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font
table->columns[i].width = 0;
}
int idx = 0;
for (Item *E : table->subitems) {
ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E);
@ -328,131 +327,11 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font
int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1;
prev_h = _resize_line(frame, i, p_base_font, p_base_font_size, w, prev_h);
}
idx++;
}
// Compute minimum width for each cell.
const int available_width = p_width - theme_cache.table_h_separation * (col_count - 1);
_set_table_size(table, available_width);
// Compute available width and total ratio (for expanders).
int total_ratio = 0;
int remaining_width = available_width;
table->total_width = theme_cache.table_h_separation;
for (int i = 0; i < col_count; i++) {
remaining_width -= table->columns[i].min_width;
if (table->columns[i].max_width > table->columns[i].min_width) {
table->columns[i].expand = true;
}
if (table->columns[i].expand) {
total_ratio += table->columns[i].expand_ratio;
}
}
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns[i].width = table->columns[i].min_width;
if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
if (i != col_count - 1) {
table->total_width += table->columns[i].width + theme_cache.table_h_separation;
} else {
table->total_width += table->columns[i].width;
}
}
// Resize to max_width if needed and distribute the remaining space.
bool table_need_fit = true;
while (table_need_fit) {
table_need_fit = false;
// Fit slim.
for (int i = 0; i < col_count; i++) {
if (!table->columns[i].expand) {
continue;
}
int dif = table->columns[i].width - table->columns[i].max_width;
if (dif > 0) {
table_need_fit = true;
table->columns[i].width = table->columns[i].max_width;
table->total_width -= dif;
total_ratio -= table->columns[i].expand_ratio;
}
}
// Grow.
remaining_width = available_width - table->total_width;
if (remaining_width > 0 && total_ratio > 0) {
for (int i = 0; i < col_count; i++) {
if (table->columns[i].expand) {
int dif = table->columns[i].max_width - table->columns[i].width;
if (dif > 0) {
int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
int incr = MIN(dif, slice);
table->columns[i].width += incr;
table->total_width += incr;
}
}
}
}
}
// Update line width and get total height.
idx = 0;
table->total_height = 0;
table->rows.clear();
table->rows_baseline.clear();
Vector2 offset;
float row_height = 0.0;
for (Item *E : table->subitems) {
ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E);
int column = idx % col_count;
offset.x += frame->padding.position.x;
float yofs = frame->padding.position.y;
float prev_h = 0;
float row_baseline = 0.0;
for (int i = 0; i < (int)frame->lines.size(); i++) {
MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
frame->lines[i].text_buf->set_width(table->columns[column].width);
table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x));
frame->lines[i].offset.y = prev_h;
float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation;
if (i > 0) {
h += theme_cache.line_separation;
}
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
if (frame->max_size_over.y > 0) {
h = MIN(h, frame->max_size_over.y);
}
yofs += h;
prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation;
frame->lines[i].offset += offset;
row_baseline = MAX(row_baseline, frame->lines[i].text_buf->get_line_ascent(frame->lines[i].text_buf->get_line_count() - 1));
}
yofs += frame->padding.size.y;
offset.x += table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x;
row_height = MAX(yofs, row_height);
if (column == col_count - 1) {
offset.x = 0;
row_height += theme_cache.table_v_separation;
table->total_height += row_height;
offset.y += row_height;
table->rows.push_back(row_height);
table->rows_baseline.push_back(table->total_height - row_height + row_baseline);
row_height = 0;
}
idx++;
}
int row_idx = (table->align_to_row < 0) ? table->rows_baseline.size() - 1 : table->align_to_row;
if (table->rows_baseline.size() != 0 && row_idx < (int)table->rows_baseline.size() - 1) {
l.text_buf->resize_object(it->rid, Size2(table->total_width, table->total_height), table->inline_align, Math::round(table->rows_baseline[row_idx]));
@ -630,127 +509,8 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
idx++;
}
// Compute available width and total ratio (for expanders).
int total_ratio = 0;
int remaining_width = available_width;
table->total_width = theme_cache.table_h_separation;
_set_table_size(table, available_width);
for (int i = 0; i < col_count; i++) {
remaining_width -= table->columns[i].min_width;
if (table->columns[i].max_width > table->columns[i].min_width) {
table->columns[i].expand = true;
}
if (table->columns[i].expand) {
total_ratio += table->columns[i].expand_ratio;
}
}
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns[i].width = table->columns[i].min_width;
if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
if (i != col_count - 1) {
table->total_width += table->columns[i].width + theme_cache.table_h_separation;
} else {
table->total_width += table->columns[i].width;
}
}
// Resize to max_width if needed and distribute the remaining space.
bool table_need_fit = true;
while (table_need_fit) {
table_need_fit = false;
// Fit slim.
for (int i = 0; i < col_count; i++) {
if (!table->columns[i].expand) {
continue;
}
int dif = table->columns[i].width - table->columns[i].max_width;
if (dif > 0) {
table_need_fit = true;
table->columns[i].width = table->columns[i].max_width;
table->total_width -= dif;
total_ratio -= table->columns[i].expand_ratio;
}
}
// Grow.
remaining_width = available_width - table->total_width;
if (remaining_width > 0 && total_ratio > 0) {
for (int i = 0; i < col_count; i++) {
if (table->columns[i].expand) {
int dif = table->columns[i].max_width - table->columns[i].width;
if (dif > 0) {
int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
int incr = MIN(dif, slice);
table->columns[i].width += incr;
table->total_width += incr;
}
}
}
}
}
// Update line width and get total height.
idx = 0;
table->total_height = 0;
table->rows.clear();
table->rows_baseline.clear();
Vector2 offset;
float row_height = 0.0;
for (const List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E->get());
int column = idx % col_count;
offset.x += frame->padding.position.x;
float yofs = frame->padding.position.y;
float prev_h = 0;
float row_baseline = 0.0;
for (int i = 0; i < (int)frame->lines.size(); i++) {
MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
frame->lines[i].text_buf->set_width(table->columns[column].width);
table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x));
frame->lines[i].offset.y = prev_h;
float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation;
if (i > 0) {
h += theme_cache.line_separation;
}
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
if (frame->max_size_over.y > 0) {
h = MIN(h, frame->max_size_over.y);
}
yofs += h;
prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation;
frame->lines[i].offset += offset;
row_baseline = MAX(row_baseline, frame->lines[i].text_buf->get_line_ascent(frame->lines[i].text_buf->get_line_count() - 1));
}
yofs += frame->padding.size.y;
offset.x += table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x;
row_height = MAX(yofs, row_height);
// Add row height after last column of the row or last cell of the table.
if (column == col_count - 1 || E->next() == nullptr) {
offset.x = 0;
row_height += theme_cache.table_v_separation;
table->total_height += row_height;
offset.y += row_height;
table->rows.push_back(row_height);
table->rows_baseline.push_back(table->total_height - row_height + row_baseline);
row_height = 0;
}
idx++;
}
int row_idx = (table->align_to_row < 0) ? table->rows_baseline.size() - 1 : table->align_to_row;
if (table->rows_baseline.size() != 0 && row_idx < (int)table->rows_baseline.size() - 1) {
l.text_buf->add_object(it->rid, Size2(table->total_width, table->total_height), table->inline_align, t_char_count, Math::round(table->rows_baseline[row_idx]));
@ -773,6 +533,132 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
return _calculate_line_vertical_offset(l);
}
void RichTextLabel::_set_table_size(ItemTable *p_table, int p_available_width) {
int col_count = p_table->columns.size();
// Compute available width and total ratio (for expanders).
int total_ratio = 0;
int remaining_width = p_available_width;
p_table->total_width = theme_cache.table_h_separation;
for (int i = 0; i < col_count; i++) {
remaining_width -= p_table->columns[i].min_width;
if (p_table->columns[i].max_width > p_table->columns[i].min_width) {
p_table->columns[i].expand = true;
}
if (p_table->columns[i].expand) {
total_ratio += p_table->columns[i].expand_ratio;
}
}
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
p_table->columns[i].width = p_table->columns[i].min_width;
if (p_table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
p_table->columns[i].width += p_table->columns[i].expand_ratio * remaining_width / total_ratio;
}
if (i != col_count - 1) {
p_table->total_width += p_table->columns[i].width + theme_cache.table_h_separation;
} else {
p_table->total_width += p_table->columns[i].width;
}
}
// Resize to max_width if needed and distribute the remaining space.
bool table_need_fit = true;
while (table_need_fit) {
table_need_fit = false;
// Fit slim.
for (int i = 0; i < col_count; i++) {
if (!p_table->columns[i].expand) {
continue;
}
int dif = p_table->columns[i].width - p_table->columns[i].max_width;
if (dif > 0) {
table_need_fit = true;
p_table->columns[i].width = p_table->columns[i].max_width;
p_table->total_width -= dif;
total_ratio -= p_table->columns[i].expand_ratio;
}
}
// Grow.
remaining_width = p_available_width - p_table->total_width;
if (remaining_width > 0 && total_ratio > 0) {
for (int i = 0; i < col_count; i++) {
if (p_table->columns[i].expand) {
int dif = p_table->columns[i].max_width - p_table->columns[i].width;
if (dif > 0) {
int slice = p_table->columns[i].expand_ratio * remaining_width / total_ratio;
int incr = MIN(dif, slice);
p_table->columns[i].width += incr;
p_table->total_width += incr;
}
}
}
}
}
// Update line width and get total height.
int idx = 0;
p_table->total_height = 0;
p_table->rows.clear();
p_table->rows_baseline.clear();
Vector2 offset;
float row_height = 0.0;
for (const List<Item *>::Element *E = p_table->subitems.front(); E; E = E->next()) {
ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E->get());
int column = idx % col_count;
offset.x += frame->padding.position.x;
float yofs = frame->padding.position.y;
float prev_h = 0;
float row_baseline = 0.0;
for (int i = 0; i < (int)frame->lines.size(); i++) {
MutexLock sub_lock(frame->lines[i].text_buf->get_mutex());
frame->lines[i].text_buf->set_width(p_table->columns[column].width);
p_table->columns[column].width = MAX(p_table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x));
frame->lines[i].offset.y = prev_h;
float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation;
if (i > 0) {
h += theme_cache.line_separation;
}
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
if (frame->max_size_over.y > 0) {
h = MIN(h, frame->max_size_over.y);
}
yofs += h;
prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation;
frame->lines[i].offset += offset;
row_baseline = MAX(row_baseline, frame->lines[i].text_buf->get_line_ascent(frame->lines[i].text_buf->get_line_count() - 1));
}
yofs += frame->padding.size.y;
offset.x += p_table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x;
row_height = MAX(yofs, row_height);
// Add row height after last column of the row or last cell of the table.
if (column == col_count - 1 || E->next() == nullptr) {
offset.x = 0;
row_height += theme_cache.table_v_separation;
p_table->total_height += row_height;
offset.y += row_height;
p_table->rows.push_back(row_height);
p_table->rows_baseline.push_back(p_table->total_height - row_height + row_baseline);
row_height = 0;
}
idx++;
}
}
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) {
ERR_FAIL_NULL_V(p_frame, 0);
ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), 0);

View file

@ -554,6 +554,8 @@ private:
float _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);
float _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h);
void _set_table_size(ItemTable *p_table, int p_available_width);
void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
int _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);
float _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 = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false, bool p_meta = false);