diff options
Diffstat (limited to 'scene/gui/rich_text_label.cpp')
-rw-r--r-- | scene/gui/rich_text_label.cpp | 171 |
1 files changed, 102 insertions, 69 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index d585fb3a7a..981766e5eb 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -142,7 +142,7 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item for (Item *it = p_item_from; it && it != p_item_to; it = _get_next_item(it)) { switch (it->type) { case ITEM_TEXT: { - ItemText *t = (ItemText *)it; + ItemText *t = static_cast<ItemText *>(it); offset += t->text.length(); if (offset > p_position) { return it; @@ -166,16 +166,16 @@ String RichTextLabel::_roman(int p_num, bool p_capitalize) const { }; String s; if (p_capitalize) { - String M[] = { "", "M", "MM", "MMM" }; - String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; - String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; - String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; + const String M[] = { "", "M", "MM", "MMM" }; + const String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; + const String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; + const String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10]; } else { - String M[] = { "", "m", "mm", "mmm" }; - String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }; - String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }; - String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }; + const String M[] = { "", "m", "mm", "mmm" }; + const String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }; + const String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }; + const String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }; s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10]; } return s; @@ -215,7 +215,7 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< RID t = l.text_buf->get_rid(); int spans = TS->shaped_get_span_count(t); for (int i = 0; i < spans; i++) { - ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, 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()) { @@ -466,15 +466,11 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> switch (it->type) { case ITEM_DROPCAP: { // Add dropcap. - const ItemDropcap *dc = (ItemDropcap *)it; - if (dc != nullptr) { - l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins); - l.dc_color = dc->color; - l.dc_ol_size = dc->ol_size; - l.dc_ol_color = dc->ol_color; - } else { - l.text_buf->clear_dropcap(); - } + const ItemDropcap *dc = static_cast<ItemDropcap *>(it); + l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins); + l.dc_color = dc->color; + l.dc_ol_size = dc->ol_size; + l.dc_ol_color = dc->ol_color; } break; case ITEM_NEWLINE: { Ref<Font> font = _find_font(it); @@ -491,7 +487,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> remaining_characters--; } break; case ITEM_TEXT: { - ItemText *t = (ItemText *)it; + ItemText *t = static_cast<ItemText *>(it); Ref<Font> font = _find_font(it); if (font.is_null()) { font = p_base_font; @@ -513,7 +509,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> l.char_count += tx.length(); } break; case ITEM_IMAGE: { - ItemImage *img = (ItemImage *)it; + ItemImage *img = static_cast<ItemImage *>(it); l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1); text += String::chr(0xfffc); l.char_count++; @@ -842,7 +838,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o // Draw inlined objects. Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { - Item *it = (Item *)(uint64_t)objects[i]; + Item *it = reinterpret_cast<Item *>((uint64_t)objects[i]); if (it != nullptr) { Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS @@ -944,8 +940,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } //Apply fx. - float faded_visibility = 1.0f; if (fade) { + float faded_visibility = 1.0f; if (glyphs[i].start >= fade->starting_index) { faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length; faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; @@ -1160,8 +1156,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } //Apply fx. - float faded_visibility = 1.0f; if (fade) { + float faded_visibility = 1.0f; if (glyphs[i].start >= fade->starting_index) { faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length; faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; @@ -1330,14 +1326,22 @@ 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) { +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; int char_pos = -1; Line &l = p_frame->lines.write[p_line]; bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); bool lrtl = is_layout_rtl(); + + // Table hit test results. bool table_hit = false; + Vector2i table_range; + float table_offy = 0.f; + ItemFrame *table_click_frame = nullptr; + int table_click_line = -1; + Item *table_click_item = nullptr; + int table_click_char = -1; for (int line = 0; line < l.text_buf->get_line_count(); line++) { RID rid = l.text_buf->get_line_rid(line); @@ -1378,10 +1382,11 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { - Item *it = (Item *)(uint64_t)objects[i]; + Item *it = reinterpret_cast<Item *>((uint64_t)objects[i]); if (it != nullptr) { Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); - if (rect.has_point(p_click - p_ofs - off)) { + rect.position += p_ofs + off; + if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { switch (it->type) { case ITEM_TABLE: { int hseparation = get_theme_constant(SNAME("table_hseparation")); @@ -1389,8 +1394,6 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V ItemTable *table = static_cast<ItemTable *>(it); - table_hit = true; - int idx = 0; int col_count = table->columns.size(); int row_count = table->rows.size(); @@ -1406,7 +1409,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (rtl) { coff.x = rect.size.width - table->columns[col].width - coff.x; } - Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size); + Rect2 crect = Rect2(rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size); if (col == col_count - 1) { if (rtl) { crect.size.x = crect.position.x + crect.size.x; @@ -1417,9 +1420,19 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } if (crect.has_point(p_click)) { for (int j = 0; j < frame->lines.size(); j++) { - _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); - if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { - return off.y; + _find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true); + if (table_click_frame && table_click_item) { + // Save cell detected cell hit data. + table_range = Vector2i(INT32_MAX, 0); + for (Item *F : table->subitems) { + ItemFrame *sub_frame = static_cast<ItemFrame *>(F); + for (int k = 0; k < sub_frame->lines.size(); k++) { + table_range.x = MIN(table_range.x, sub_frame->lines[k].char_offset); + table_range.y = MAX(table_range.y, sub_frame->lines[k].char_offset + sub_frame->lines[k].char_count); + } + } + table_offy = off.y; + table_hit = true; } } } @@ -1433,14 +1446,39 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } } } - Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y)); + Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)) - p_frame->padding.position, TS->shaped_text_get_size(rid) + p_frame->padding.position + p_frame->padding.size); + if (p_table) { + rect.size.y += get_theme_constant(SNAME("table_vseparation")); + } - if (rect.has_point(p_click) && !table_hit) { + 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 table hit was detected, and line hit is in the table bounds use table hit. + if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) { + if (r_click_frame != nullptr) { + *r_click_frame = table_click_frame; + } + + if (r_click_line != nullptr) { + *r_click_line = table_click_line; + } + + if (r_click_item != nullptr) { + *r_click_item = table_click_item; + } + + if (r_click_char != nullptr) { + *r_click_char = table_click_char; + } + return table_offy; + } + off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation")); } + // Text line hit. if (char_pos >= 0) { // Find item. if (r_click_item != nullptr) { @@ -1451,7 +1489,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (it_to != nullptr) { *r_click_item = _get_prev_item(it_to); } else { - for (Item *i = it; i && i != it_to; i = _get_next_item(i)) { + for (Item *i = it; i; i = _get_next_item(i)) { *r_click_item = i; } } @@ -1650,8 +1688,7 @@ void RichTextLabel::_notification(int p_what) { case NOTIFICATION_FOCUS_EXIT: { if (deselect_on_focus_loss_enabled) { - selection.active = false; - update(); + deselect(); } } break; @@ -1684,9 +1721,9 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const Item *item = nullptr; bool outside = true; - ((RichTextLabel *)(this))->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); + const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); - if (item && !outside && ((RichTextLabel *)(this))->_find_meta(item, nullptr)) { + if (item && !outside && const_cast<RichTextLabel *>(this)->_find_meta(item, nullptr)) { return CURSOR_POINTING_HAND; } @@ -1742,9 +1779,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_line = 0; selection.to_item = nullptr; selection.to_char = 0; - selection.active = false; - - update(); + deselect(); } } } @@ -1802,9 +1837,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_line = 0; selection.to_item = nullptr; selection.to_char = 0; - selection.active = false; - - update(); + deselect(); } } @@ -1919,14 +1952,13 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_char = c_index; bool swap = false; - if (selection.from_item->index > selection.to_item->index) { - swap = true; - } else if (selection.from_item->index == selection.to_item->index) { - if (selection.from_char > selection.to_char) { + if (selection.click_frame && c_frame) { + const Line &l1 = c_frame->lines[c_line]; + const Line &l2 = selection.click_frame->lines[selection.click_line]; + if (l1.char_offset + c_index < l2.char_offset + selection.click_char) { swap = true; - } else if (selection.from_char == selection.to_char) { - selection.active = false; - update(); + } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char) { + deselect(); return; } } @@ -1988,7 +2020,7 @@ void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) while (item) { if (item->parent != nullptr && item->parent->type == ITEM_FRAME) { if (r_frame != nullptr) { - *r_frame = (ItemFrame *)item->parent; + *r_frame = static_cast<ItemFrame *>(item->parent); } if (r_line != nullptr) { *r_line = item->line; @@ -2523,7 +2555,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) p_item->index = current_idx++; p_item->char_ofs = current_char_ofs; if (p_item->type == ITEM_TEXT) { - ItemText *t = (ItemText *)p_item; + ItemText *t = static_cast<ItemText *>(p_item); current_char_ofs += t->text.length(); } else if (p_item->type == ITEM_IMAGE) { current_char_ofs++; @@ -2977,11 +3009,10 @@ void RichTextLabel::clear() { main->lines.clear(); main->lines.resize(1); main->first_invalid_line = 0; - update(); selection.click_frame = nullptr; selection.click_item = nullptr; - selection.active = false; + deselect(); current_idx = 1; current_char_ofs = 0; @@ -3890,8 +3921,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { selection.enabled = p_enabled; if (!p_enabled) { if (selection.active) { - selection.active = false; - update(); + deselect(); } set_focus_mode(FOCUS_NONE); } else { @@ -3902,8 +3932,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) { deselect_on_focus_loss_enabled = p_enabled; if (p_enabled && selection.active && !has_focus()) { - selection.active = false; - update(); + deselect(); } } @@ -3967,7 +3996,7 @@ bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p text += "\n"; } break; case ITEM_TEXT: { - ItemText *t = (ItemText *)it; + ItemText *t = static_cast<ItemText *>(it); text += t->text; } break; case ITEM_IMAGE: { @@ -4100,7 +4129,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p if (it_to != nullptr) { end_idx = it_to->index; } else { - for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + for (Item *it = l.from; it; it = _get_next_item(it)) { end_idx = it->index + 1; } } @@ -4154,6 +4183,11 @@ String RichTextLabel::get_selected_text() const { return text; } +void RichTextLabel::deselect() { + selection.active = false; + update(); +} + void RichTextLabel::selection_copy() { String text = get_selected_text(); @@ -4218,10 +4252,8 @@ String RichTextLabel::get_parsed_text() const { Item *it = main; while (it) { if (it->type == ITEM_DROPCAP) { - const ItemDropcap *dc = (ItemDropcap *)it; - if (dc != nullptr) { - text += dc->text; - } + ItemDropcap *dc = static_cast<ItemDropcap *>(it); + text += dc->text; } else if (it->type == ITEM_TEXT) { ItemText *t = static_cast<ItemText *>(it); text += t->text; @@ -4460,6 +4492,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to); ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text); + ClassDB::bind_method(D_METHOD("deselect"), &RichTextLabel::deselect); ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode); ClassDB::bind_method(D_METHOD("append_text", "bbcode"), &RichTextLabel::append_text); @@ -4500,7 +4533,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects); ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect); - // Note: set "bbcode_enabled" first, to avoid unnecessery "text" resets. + // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); @@ -4707,7 +4740,7 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item // Draw a box based on color tags associated with glyphs for (int i = start; i < end; i++) { Item *it = _get_item_at_pos(it_from, it_to, i); - Color color = Color(0, 0, 0, 0); + Color color; if (fbg_flag == 0) { color = _find_bgcolor(it); |