diff options
Diffstat (limited to 'scene/gui/rich_text_label.cpp')
-rw-r--r-- | scene/gui/rich_text_label.cpp | 156 |
1 files changed, 132 insertions, 24 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 244d90ca6b..4f4d5ece4a 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -404,6 +404,18 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> break; } 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(); + } + } break; case ITEM_NEWLINE: { Ref<Font> font = _find_font(it); if (font.is_null()) { @@ -606,11 +618,11 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } } -float 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_color_shadow, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) { +void 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_color_shadow, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) { Vector2 off; - ERR_FAIL_COND_V(p_frame == nullptr, off.y); - ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), off.y); + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); Line &l = p_frame->lines.write[p_line]; @@ -619,7 +631,7 @@ float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p Variant meta; if (it_from == nullptr) { - return off.y; + return; } RID ci = get_canvas_item(); @@ -679,6 +691,14 @@ float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p } } + // Draw dropcap. + int dc_lines = l.text_buf->get_dropcap_lines(); + float h_off = l.text_buf->get_dropcap_size().x; + if (l.dc_ol_size > 0) { + l.text_buf->draw_dropcap_outline(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color); + } + l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color); + // Draw text. for (int line = 0; line < l.text_buf->get_line_count(); line++) { RID rid = l.text_buf->get_line_rid(line); @@ -718,6 +738,14 @@ float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p } break; } + if (line <= dc_lines) { + if (rtl) { + off.x -= h_off; + } else { + off.x += h_off; + } + } + //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); @@ -1040,8 +1068,6 @@ float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p } off.y += TS->shaped_text_get_descent(rid); } - - return off.y; } void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) { @@ -1065,7 +1091,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + _get_text_rect().get_position().y >= vofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { break; } from_line++; @@ -1250,7 +1276,7 @@ void RichTextLabel::_update_scroll() { scroll_visible = true; scroll_w = vscroll->get_combined_minimum_size().width; vscroll->show(); - vscroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -scroll_w); + vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w); } else { scroll_visible = false; scroll_w = 0; @@ -1349,7 +1375,7 @@ void RichTextLabel::_notification(int p_what) { //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + _get_text_rect().get_position().y >= vofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { break; } from_line++; @@ -1372,7 +1398,8 @@ void RichTextLabel::_notification(int p_what) { Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); while (ofs.y < size.height && from_line < main->lines.size()) { visible_line_count++; - ofs.y += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs); + _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs); + ofs.y += main->lines[from_line].text_buf->get_size().y; from_line++; } } break; @@ -1743,6 +1770,19 @@ Dictionary RichTextLabel::_find_font_features(Item *p_item) { return Dictionary(); } +RichTextLabel::ItemDropcap *RichTextLabel::_find_dc_item(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_DROPCAP) { + return static_cast<ItemDropcap *>(item); + } + item = item->parent; + } + + return nullptr; +} + RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) { Item *item = p_item; @@ -2062,14 +2102,14 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; } p_frame->first_resized_line = p_frame->lines.size(); updating_scroll = true; vscroll->set_max(total_height); - vscroll->set_page(size.height); + vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height - size.height); } @@ -2098,7 +2138,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; } p_frame->first_invalid_line = p_frame->lines.size(); @@ -2106,7 +2146,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { updating_scroll = true; vscroll->set_max(total_height); - vscroll->set_page(size.height); + vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height - size.height); } @@ -2317,6 +2357,24 @@ bool RichTextLabel::remove_line(const int p_line) { return true; } +void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins, const Color &p_color, int p_ol_size, const Color &p_ol_color) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ERR_FAIL_COND(p_string.is_empty()); + ERR_FAIL_COND(p_font.is_null()); + ERR_FAIL_COND(p_size <= 0); + + ItemDropcap *item = memnew(ItemDropcap); + + item->text = p_string; + item->font = p_font; + item->font_size = p_size; + item->color = p_color; + item->ol_size = p_ol_size; + item->ol_color = p_ol_color; + item->dropcap_margins = p_dropcap_margins; + _add_item(item, false); +} + void RichTextLabel::push_font(const Ref<Font> &p_font) { ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(p_font.is_null()); @@ -2724,7 +2782,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { String bbcode_name; typedef Map<String, String> OptionMap; OptionMap bbcode_options; - if (!split_tag_block.empty()) { + if (!split_tag_block.is_empty()) { bbcode_name = split_tag_block[0]; for (int i = 1; i < split_tag_block.size(); i++) { const String &expr = split_tag_block[i]; @@ -2766,7 +2824,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { tag_stack.pop_front(); pos = brk_end + 1; - if (tag != "/img") { + if (tag != "/img" && tag != "/dropcap") { pop(); } @@ -3042,6 +3100,54 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { push_meta(url); pos = brk_end + 1; tag_stack.push_front("url"); + } else if (tag.begins_with("dropcap")) { + Vector<String> subtag = tag.substr(5, tag.length()).split(" "); + Ref<Font> f = get_theme_font("normal_font"); + int fs = get_theme_font_size("normal_font_size") * 3; + Color color = get_theme_color("default_color"); + Color outline_color = get_theme_color("outline_color"); + int outline_size = get_theme_constant("outline_size"); + Rect2 dropcap_margins = Rect2(); + + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + if (subtag_a[0] == "font" || subtag_a[0] == "f") { + String fnt = subtag_a[1]; + Ref<Font> font = ResourceLoader::load(fnt, "Font"); + if (font.is_valid()) { + f = font; + } + } else if (subtag_a[0] == "font_size") { + fs = subtag_a[1].to_int(); + } else if (subtag_a[0] == "margins") { + Vector<String> subtag_b = subtag_a[1].split(","); + if (subtag_b.size() == 4) { + dropcap_margins.position.x = subtag_b[0].to_float(); + dropcap_margins.position.y = subtag_b[1].to_float(); + dropcap_margins.size.x = subtag_b[2].to_float(); + dropcap_margins.size.y = subtag_b[3].to_float(); + } + } else if (subtag_a[0] == "outline_size") { + outline_size = subtag_a[1].to_int(); + } else if (subtag_a[0] == "color") { + color = _get_color_from_string(subtag_a[1], color); + } else if (subtag_a[0] == "outline_color") { + outline_color = _get_color_from_string(subtag_a[1], outline_color); + } + } + } + int end = p_bbcode.find("[", brk_end); + if (end == -1) { + end = p_bbcode.length(); + } + + String txt = p_bbcode.substr(brk_end + 1, end - brk_end - 1); + + push_dropcap(txt, f, fs, dropcap_margins, color, outline_size, outline_color); + + pos = end; + tag_stack.push_front(bbcode_name); } else if (tag.begins_with("img")) { VAlign align = VALIGN_TOP; if (tag.begins_with("img=")) { @@ -3072,7 +3178,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { int width = 0; int height = 0; - if (!bbcode_value.empty()) { + if (!bbcode_value.is_empty()) { int sep = bbcode_value.find("x"); if (sep == -1) { width = bbcode_value.to_int(); @@ -3649,7 +3755,7 @@ void RichTextLabel::install_effect(const Variant effect) { int RichTextLabel::get_content_height() const { int total_height = 0; if (main->lines.size()) { - total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y; } return total_height; } @@ -3680,6 +3786,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP)); + ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color); @@ -3815,8 +3922,9 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_WAVE); BIND_ENUM_CONSTANT(ITEM_TORNADO); BIND_ENUM_CONSTANT(ITEM_RAINBOW); - BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); BIND_ENUM_CONSTANT(ITEM_META); + BIND_ENUM_CONSTANT(ITEM_DROPCAP); + BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); } void RichTextLabel::set_visible_characters(int p_visible) { @@ -3956,9 +4064,9 @@ RichTextLabel::RichTextLabel() { add_child(vscroll); vscroll->set_drag_node(String("..")); vscroll->set_step(1); - vscroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0); - vscroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); - vscroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); + vscroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); + vscroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); + vscroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); vscroll->connect("value_changed", callable_mp(this, &RichTextLabel::_scroll_changed)); vscroll->set_step(1); vscroll->hide(); |