From 13c2c0bbc26f67cb38b17e2fd8bb6369cb0a0327 Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Fri, 29 May 2020 16:25:12 +0200 Subject: [PATCH 1/9] Remove bbcode_text from RichTextLabel This simplyfies the text setting and resolves some bugs --- scene/gui/rich_text_label.cpp | 19 ++++++++++--------- scene/gui/rich_text_label.h | 6 +++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 2f5af0eda0db..a79f9b3b5ec0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -45,7 +45,7 @@ #include "editor/editor_scale.h" #endif -RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { +RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { return p_item->subitems.front()->get(); @@ -90,7 +90,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { return nullptr; } -RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) { +RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { return p_item->subitems.back()->get(); @@ -2551,7 +2551,11 @@ void RichTextLabel::set_bbcode(const String &p_bbcode) { } String RichTextLabel::get_bbcode() const { - return bbcode; + if (use_bbcode) { + return bbcode; + } else { + return get_text(); + } } void RichTextLabel::set_use_bbcode(bool p_enable) { @@ -2566,7 +2570,7 @@ bool RichTextLabel::is_using_bbcode() const { return use_bbcode; } -String RichTextLabel::get_text() { +String RichTextLabel::get_text() const { String text = ""; Item *it = main; while (it) { @@ -2718,16 +2722,13 @@ 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); - ADD_GROUP("BBCode", "bbcode_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 4cec43556860..64c3b1c73f17 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -389,8 +389,8 @@ class RichTextLabel : public Control { void _scroll_changed(double); void _gui_input(Ref p_event); - Item *_get_next_item(Item *p_item, bool p_free = false); - Item *_get_prev_item(Item *p_item, bool p_free = false); + Item *_get_next_item(Item *p_item, bool p_free = false) const; + Item *_get_prev_item(Item *p_item, bool p_free = false) const; Rect2 _get_text_rect(); Ref _get_custom_effect_by_code(String p_bbcode_identifier); @@ -407,7 +407,7 @@ class RichTextLabel : public Control { void _notification(int p_what); public: - String get_text(); + String get_text() const; void add_text(const String &p_text); void add_image(const Ref &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0)); void add_newline(); From d98d3287fd23534691859c46e4b2c8636db5aa4d Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Wed, 3 Jun 2020 00:02:22 +0200 Subject: [PATCH 2/9] Move declarations into Header and comment on rewrite changes --- scene/gui/rich_text_label.cpp | 20 ---------- scene/gui/rich_text_label.h | 70 ++++++++++++++++++++++------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a79f9b3b5ec0..2648967586d9 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2885,19 +2885,6 @@ RichTextLabel::RichTextLabel() { main->lines.write[0].from = main; main->first_invalid_line = 0; current_frame = main; - tab_size = 4; - default_align = ALIGN_LEFT; - underline_meta = true; - meta_hovering = nullptr; - override_selected_font_color = false; - - scroll_visible = false; - scroll_follow = false; - scroll_following = false; - updating_scroll = false; - scroll_active = true; - scroll_w = 0; - scroll_updated = false; vscroll = memnew(VScrollBar); add_child(vscroll); @@ -2909,18 +2896,11 @@ RichTextLabel::RichTextLabel() { vscroll->connect("value_changed", callable_mp(this, &RichTextLabel::_scroll_changed)); vscroll->set_step(1); vscroll->hide(); - current_idx = 1; - use_bbcode = false; selection.click = nullptr; selection.active = false; selection.enabled = false; - visible_characters = -1; - percent_visible = 1; - visible_line_count = 0; - - fixed_width = -1; set_clip_contents(true); } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 64c3b1c73f17..fe75ab05bacd 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -32,10 +32,11 @@ #define RICH_TEXT_LABEL_H #include "rich_text_effect.h" +#include "scene/gui/label.h" #include "scene/gui/scroll_bar.h" -class RichTextLabel : public Control { - GDCLASS(RichTextLabel, Control); +class RichTextLabel : public Label { + GDCLASS(RichTextLabel, Label); public: enum Align { @@ -80,6 +81,8 @@ class RichTextLabel : public Control { static void _bind_methods(); private: + //Do we need all the structs in the implementation here, or can they be completly in the bbcode implementation + //We should aim for the latter struct Item; struct Line { @@ -312,23 +315,27 @@ class RichTextLabel : public Control { VScrollBar *vscroll; - bool scroll_visible; - bool scroll_follow; - bool scroll_following; - bool scroll_active; - int scroll_w; - bool scroll_updated; - bool updating_scroll; - int current_idx; - int visible_line_count; - - int tab_size; - bool underline_meta; - bool override_selected_font_color; - - Align default_align; - - ItemMeta *meta_hovering; + bool scroll_visible = false; + bool scroll_follow = false; + bool scroll_following = false; + bool scroll_active = true; + int scroll_w = 0; + bool scroll_updated = false; + bool updating_scroll = false; + int current_idx = 1; + int visible_line_count = 0; + + //duplicated into bbcode + int tab_size = 4; + //duplicated into bbcode + bool underline_meta = true; + //duplicated into bbcode + bool override_selected_font_color = false; + + //duplicated into bbcode. Never changed. Make const? + Align default_align = ALIGN_LEFT; + + ItemMeta *meta_hovering = nullptr; Variant current_meta; Vector> custom_effects; @@ -362,24 +369,33 @@ class RichTextLabel : public Control { bool active; // anything selected? i.e. from, to, etc. valid? bool enabled; // allow selections? }; - + //duplicated into bbcode Selection selection; - - int visible_characters; - float percent_visible; + //duplicated into bbcode + int visible_characters = -1; + float percent_visible = 1; int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); + //used in _process_line, _find_margin, search + //duplicated in bbcode, check removal Ref _find_font(Item *p_item); + // used once in _process_line int _find_margin(Item *p_item, const Ref &p_base_font); + // used once in _process_line Align _find_align(Item *p_item); + // only used in _procces_line Color _find_color(Item *p_item, const Color &p_default_color); + //used once in _process_line bool _find_underline(Item *p_item); + //used once in _process_line bool _find_strikethrough(Item *p_item); + //used multiple times bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); bool _find_layout_subitem(Item *from, Item *to); bool _find_by_type(Item *p_item, ItemType p_type); + //only used once in _process_line, duplicated into bbcode void _fetch_item_fx_stack(Item *p_item, Vector &r_stack); static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color); @@ -396,12 +412,16 @@ class RichTextLabel : public Control { Ref _get_custom_effect_by_code(String p_bbcode_identifier); virtual Dictionary parse_expressions_for_values(Vector p_expressions); - bool use_bbcode; + bool use_bbcode = false; String bbcode; void _update_all_lines(); - int fixed_width; + int fixed_width = -1; + + //Temporary to make it compile asap + //Check later, what belongs in which class and fix dependencies and move stuff around + friend class BbCodeParser; protected: void _notification(int p_what); From 33dce22dd0d4e080a24b62e5c05c60c480a6a7f9 Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Wed, 3 Jun 2020 00:04:58 +0200 Subject: [PATCH 3/9] Move BBCode into it's own class (ongoing Refractor on RTL) With this commit the engine is compiling, but the class itself doesn't draw. There are changes which need to be made undone, but the priority in this commit was compilability. The inlined defines were refractored into their own functions. --- scene/gui/bbcode.cpp | 981 +++++++++++++++++++++++++++++++++++++++++++ scene/gui/bbcode.h | 131 ++++++ 2 files changed, 1112 insertions(+) create mode 100644 scene/gui/bbcode.cpp create mode 100644 scene/gui/bbcode.h diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp new file mode 100644 index 000000000000..795283a8c8cc --- /dev/null +++ b/scene/gui/bbcode.cpp @@ -0,0 +1,981 @@ + +#include "scene/gui/bbcode.h" +#include "core/math/math_defs.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "scene/scene_string_names.h" +#include "servers/display_server.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_scale.h" +#endif + +BbCodeParser::BbCodeParser() {} + +BbCodeParser::~BbCodeParser() {} + +bool BbCodeParser::_new_line() { + if (p_mode != ProcessMode::PROCESS_CACHE) { + line++; + backtrack = 0; + if (!line_is_blank) { + nonblank_line_count++; + } + line_is_blank = true; + if (line < l.offset_caches.size()) + line_ofs = l.offset_caches[line]; + wofs = margin; + if (align != Align::ALIGN_FILL) + wofs += line_ofs; + } else { + int used = wofs - margin; + switch (align) { + case Align::ALIGN_LEFT: + l.offset_caches.push_back(0); + break; + case Align::ALIGN_CENTER: + l.offset_caches.push_back(((p_width - margin) - used) / 2); + break; + case Align::ALIGN_RIGHT: + l.offset_caches.push_back(((p_width - margin) - used)); + break; + case Align::ALIGN_FILL: + l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); + break; + } + l.height_caches.push_back(line_height); + l.ascent_caches.push_back(line_ascent); + l.descent_caches.push_back(line_descent); + l.space_caches.push_back(spaces); + } + line_wrapped = false; + p_height += line_height + get_theme_constant(SceneStringNames::get_singleton()->line_separation); + line_height = 0; + line_ascent = 0; + line_descent = 0; + spaces = 0; + spaces_size = 0; + wofs = begin; + align_ofs = 0; + if (p_mode != ProcessMode::PROCESS_CACHE) { + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; + line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; + } + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh && p_click_pos.x < p_ofs.x + wofs) { + if (r_outside) + *r_outside = true; + *r_click_item = it; + *r_click_char = rchar; + return true; + } + return false; +} + +bool BbCodeParser::_ensure_width(int m_width) { + if (p_mode == ProcessMode::PROCESS_CACHE) { + l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); + l.minimum_width = MAX(l.minimum_width, m_width); + } + if (wofs - backtrack + m_width > p_width) { + line_wrapped = true; + if (p_mode == ProcessMode::PROCESS_CACHE) { + if (spaces > 0) + spaces -= 1; + } + const bool x_in_range = (p_click_pos.x > p_ofs.x + wofs) && (!p_frame->cell || p_click_pos.x < p_ofs.x + p_width); + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh && x_in_range) { + if (r_outside) + *r_outside = true; + *r_click_item = it; + *r_click_char = rchar; + return true; + } + return _new_line(); + } + return false; +} + +bool BbCodeParser::_advance(int m_width) { + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh && p_click_pos.x >= p_ofs.x + wofs && p_click_pos.x < p_ofs.x + wofs + m_width) { + if (r_outside) + *r_outside = false; + *r_click_item = it; + *r_click_char = rchar; + return true; + } + wofs += m_width; + return false; +} + +void BbCodeParser::_check_height(int m_height) { + if (m_height > line_height) { + line_height = m_height; + } +} + +bool BbCodeParser::_y_range_visible(int m_top, int m_height) { + return (m_height > 0 && ((m_top >= 0 && m_top < height) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < height))); +} + +BbCodeParser::Item *BbCodeParser::_get_next_item(Item *p_item, bool p_free) const { + if (p_free) { + if (p_item->subitems.size()) { + return p_item->subitems.front()->get(); + } else if (!p_item->parent) { + return nullptr; + } else if (p_item->E->next()) { + return p_item->E->next()->get(); + } else { + //go up until something with a next is found + while (p_item->parent && !p_item->E->next()) { + p_item = p_item->parent; + } + + if (p_item->parent) { + return p_item->E->next()->get(); + } else { + return nullptr; + } + } + + } else { + if (p_item->subitems.size() && p_item->type != ItemType::ITEM_TABLE) { + return p_item->subitems.front()->get(); + } else if (p_item->type == ItemType::ITEM_FRAME) { + return nullptr; + } else if (p_item->E->next()) { + return p_item->E->next()->get(); + } else { + //go up until something with a next is found + while (p_item->type != ItemType::ITEM_FRAME && !p_item->E->next()) { + p_item = p_item->parent; + } + + if (p_item->type != ItemType::ITEM_FRAME) { + return p_item->E->next()->get(); + } else { + return nullptr; + } + } + } + + return nullptr; +} + +Ref BbCodeParser::_find_font(Item *p_item) { + Item *fontitem = p_item; + + while (fontitem) { + if (fontitem->type == ItemType::ITEM_FONT) { + ItemFont *fi = static_cast(fontitem); + return fi->font; + } + + fontitem = fontitem->parent; + } + + return Ref(); +} + +int BbCodeParser::_find_margin(Item *p_item, const Ref &p_base_font) { + Item *item = p_item; + + int margin = 0; + + while (item) { + if (item->type == ItemType::ITEM_INDENT) { + Ref font = _find_font(item); + if (font.is_null()) { + font = p_base_font; + } + + ItemIndent *indent = static_cast(item); + + margin += indent->level * tab_size * font->get_char_size(' ').width; + + } else if (item->type == ItemType::ITEM_LIST) { + Ref font = _find_font(item); + if (font.is_null()) { + font = p_base_font; + } + } + + item = item->parent; + } + + return margin; +} + +BbCodeParser::Align BbCodeParser::_find_align(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ItemType::ITEM_ALIGN) { + ItemAlign *align = static_cast(item); + return align->align; + } + + item = item->parent; + } + + return default_align; +} + +Color BbCodeParser::_find_color(Item *p_item, const Color &p_default_color) { + Item *item = p_item; + + while (item) { + if (item->type == ItemType::ITEM_COLOR) { + ItemColor *color = static_cast(item); + return color->color; + } + + item = item->parent; + } + + return p_default_color; +} + +bool BbCodeParser::_find_underline(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ItemType::ITEM_UNDERLINE) { + return true; + } + + item = item->parent; + } + + return false; +} + +bool BbCodeParser::_find_strikethrough(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ItemType::ITEM_STRIKETHROUGH) { + return true; + } + + item = item->parent; + } + + return false; +} + +bool BbCodeParser::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { + Item *item = p_item; + + while (item) { + if (item->type == ItemType::ITEM_META) { + ItemMeta *meta = static_cast(item); + if (r_meta) { + *r_meta = meta->meta; + } + if (r_item) { + *r_item = meta; + } + return true; + } + + item = item->parent; + } + + return false; +} + +void BbCodeParser::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) { + Item *item = p_item; + while (item) { + if (item->type == ItemType::ITEM_CUSTOMFX || item->type == ItemType::ITEM_SHAKE || item->type == ItemType::ITEM_WAVE || item->type == ItemType::ITEM_TORNADO || item->type == ItemType::ITEM_RAINBOW) { + r_stack.push_back(static_cast(item)); + } + + item = item->parent; + } +} + +int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { + ERR_FAIL_INDEX_V((int)p_mode, 3, 0); + + RID ci; + if (r_outside) { + *r_outside = false; + } + if (p_mode == ProcessMode::PROCESS_DRAW) { + ci = get_canvas_item(); + + if (r_click_item) { + *r_click_item = nullptr; + } + } + Line &l = p_frame->lines.write[p_line]; + Item *it = l.from; + + int line_ofs = 0; + int margin = _find_margin(it, p_base_font); + RichTextLabel::Align align = _find_align(it); + int line = 0; + int spaces = 0; + + int height = get_size().y; + + if (p_mode != ProcessMode::PROCESS_CACHE) { + ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0); + line_ofs = l.offset_caches[line]; + } + + if (p_mode == ProcessMode::PROCESS_CACHE) { + l.offset_caches.clear(); + l.height_caches.clear(); + l.ascent_caches.clear(); + l.descent_caches.clear(); + l.char_count = 0; + l.minimum_width = 0; + l.maximum_width = 0; + } + + int wofs = margin; + int spaces_size = 0; + int align_ofs = 0; + + if (p_mode != ProcessMode::PROCESS_CACHE && align != Align::ALIGN_FILL) { + wofs += line_ofs; + } + + int begin = wofs; + + Ref cfont = _find_font(it); + if (cfont.is_null()) { + cfont = p_base_font; + } + + //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed + int line_height = cfont->get_height(); + int line_ascent = cfont->get_ascent(); + int line_descent = cfont->get_descent(); + + int backtrack = 0; // for dynamic hidden content. + + int nonblank_line_count = 0; //number of nonblank lines as counted during ProcessMode::PROCESS_DRAW + + Variant meta; + + Color selection_fg; + Color selection_bg; + + if (p_mode == RichTextLabel::ProcessMode::PROCESS_DRAW) { + selection_fg = get_theme_color("font_color_selected"); + selection_bg = get_theme_color("selection_color"); + } + + int rchar = 0; + int lh = 0; + bool line_is_blank = true; + bool line_wrapped = false; + int fh = 0; + + while (it) { + switch (it->type) { + case ItemType::ITEM_ALIGN: { + ItemAlign *align_it = static_cast(it); + + align = align_it->align; + + } break; + case ItemType::ITEM_INDENT: { + if (it != l.from) { + ItemIndent *indent_it = static_cast(it); + + int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; + margin += indent; + begin += indent; + wofs += indent; + } + + } break; + case ItemType::ITEM_TEXT: { + ItemText *text = static_cast(it); + + Ref font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + + const CharType *c = text->text.c_str(); + const CharType *cf = c; + int ascent = font->get_ascent(); + int descent = font->get_descent(); + + Color color; + Color font_color_shadow; + bool underline = false; + bool strikethrough = false; + ItemFade *fade = nullptr; + int it_char_start = p_char_count; + + Vector fx_stack = Vector(); + _fetch_item_fx_stack(text, fx_stack); + bool custom_fx_ok = true; + + if (p_mode == ProcessMode::PROCESS_DRAW) { + color = _find_color(text, p_base_color); + font_color_shadow = _find_color(text, p_font_color_shadow); + if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { + underline = true; + } else if (_find_strikethrough(text)) { + strikethrough = true; + } + + Item *fade_item = it; + while (fade_item) { + if (fade_item->type == ItemType::ITEM_FADE) { + fade = static_cast(fade_item); + break; + } + fade_item = fade_item->parent; + } + + } else if (p_mode == ProcessMode::PROCESS_CACHE) { + l.char_count += text->text.length(); + } + + rchar = 0; + FontDrawer drawer(font, Color(1, 1, 1)); + while (*c) { + int end = 0; + int w = 0; + int fw = 0; + + lh = 0; + + if (p_mode != ProcessMode::PROCESS_CACHE) { + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; + line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; + } + while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) { + int cw = font->get_char_size(c[end], c[end + 1]).width; + if (c[end] == '\t') { + cw = tab_size * font->get_char_size(' ').width; + } + + if (end > 0 && fw + cw + begin > p_width) { + break; //don't allow lines longer than assigned width + } + + fw += cw; + + end++; + } + _check_height(fh); + if (_ensure_width(fw)) { + return nonblank_line_count; + } + + line_ascent = MAX(line_ascent, ascent); + line_descent = MAX(line_descent, descent); + fh = line_ascent + line_descent; + + if (end && c[end - 1] == ' ') { + if (p_mode == ProcessMode::PROCESS_CACHE) { + spaces_size += font->get_char_size(' ').width; + } else if (align == Align::ALIGN_FILL) { + int ln = MIN(l.offset_caches.size() - 1, line); + if (l.space_caches[ln]) { + align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln]; + } + } + spaces++; + } + + { + int ofs = 0 - backtrack; + + for (int i = 0; i < end; i++) { + int pofs = wofs + ofs; + + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { + int cw = font->get_char_size(c[i], c[i + 1]).x; + + if (c[i] == '\t') { + cw = tab_size * font->get_char_size(' ').width; + } + + if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) { + rchar = int((&c[i]) - cf); + } + + ofs += cw; + } else if (p_mode == ProcessMode::PROCESS_DRAW) { + bool selected = false; + Color fx_color = Color(color); + Point2 fx_offset; + CharType fx_char = c[i]; + + if (selection.active) { + int cofs = (&c[i]) - cf; + if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) { + selected = true; + } + } + + int cw = 0; + int c_item_offset = p_char_count - it_char_start; + + float faded_visibility = 1.0f; + if (fade) { + if (c_item_offset >= fade->starting_index) { + faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; + faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; + } + fx_color.a = faded_visibility; + } + + bool visible = visible_characters < 0 || ((p_char_count < visible_characters && _y_range_visible(y + lh - line_descent - line_ascent, line_ascent + line_descent)) && + faded_visibility > 0.0f); + + const bool previously_visible = visible; + + for (int j = 0; j < fx_stack.size(); j++) { + ItemFX *item_fx = fx_stack[j]; + + if (item_fx->type == ItemType::ITEM_CUSTOMFX && custom_fx_ok) { + ItemCustomFX *item_custom = static_cast(item_fx); + + Ref charfx = item_custom->char_fx_transform; + Ref custom_effect = item_custom->custom_effect; + + if (!custom_effect.is_null()) { + charfx->elapsed_time = item_custom->elapsed_time; + charfx->relative_index = c_item_offset; + charfx->absolute_index = p_char_count; + charfx->visibility = visible; + charfx->offset = fx_offset; + charfx->color = fx_color; + charfx->character = fx_char; + + bool effect_status = custom_effect->_process_effect_impl(charfx); + custom_fx_ok = effect_status; + + fx_offset += charfx->offset; + fx_color = charfx->color; + visible &= charfx->visibility; + fx_char = charfx->character; + } + } else if (item_fx->type == ItemType::ITEM_SHAKE) { + ItemShake *item_shake = static_cast(item_fx); + + uint64_t char_current_rand = item_shake->offset_random(c_item_offset); + uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + fx_offset += Point2(Math::lerp(Math::sin(previous_offset), + Math::sin(current_offset), + n_time), + Math::lerp(Math::cos(previous_offset), + Math::cos(current_offset), + n_time)) * + (float)item_shake->strength / 10.0f; + } else if (item_fx->type == ItemType::ITEM_WAVE) { + ItemWave *item_wave = static_cast(item_fx); + + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); + fx_offset += Point2(0, 1) * value; + } else if (item_fx->type == ItemType::ITEM_TORNADO) { + ItemTornado *item_tornado = static_cast(item_fx); + + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + fx_offset += Point2(torn_x, torn_y); + } else if (item_fx->type == ItemType::ITEM_RAINBOW) { + ItemRainbow *item_rainbow = static_cast(item_fx); + + fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), + item_rainbow->saturation, + item_rainbow->value, + fx_color.a); + } + } + + if (visible) { + line_is_blank = false; + w += font->get_char_size(c[i], c[i + 1]).x; + } + + if (c[i] == '\t') { + visible = false; + } + + if (visible) { + if (selected) { + cw = font->get_char_size(fx_char, c[i + 1]).x; + draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); + } + + if (p_font_color_shadow.a > 0) { + float x_ofs_shadow = align_ofs + pofs; + float y_ofs_shadow = y + lh - line_descent; + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + + if (p_shadow_as_outline) { + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + } + } + + if (selected) { + drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); + } else { + cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); + } + } else if (previously_visible && c[i] != '\t') { + backtrack += font->get_char_size(fx_char, c[i + 1]).x; + } + + p_char_count++; + if (c[i] == '\t') { + cw = tab_size * font->get_char_size(' ').width; + backtrack = MAX(0, backtrack - cw); + } + + ofs += cw; + } + } + + if (underline) { + Color uc = color; + uc.a *= 0.5; + int uy = y + lh - line_descent + font->get_underline_position(); + float underline_width = font->get_underline_thickness(); +#ifdef TOOLS_ENABLED + underline_width *= EDSCALE; +#endif + RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); + } else if (strikethrough) { + Color uc = color; + uc.a *= 0.5; + int uy = y + lh - (line_ascent + line_descent) / 2; + float strikethrough_width = font->get_underline_thickness(); +#ifdef TOOLS_ENABLED + strikethrough_width *= EDSCALE; +#endif + RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width); + } + } + + if (_advance(fw)) { + return nonblank_line_count; + } + _check_height(fh); //must be done somewhere + c = &c[end]; + } + + } break; + case ItemType::ITEM_IMAGE: { + lh = 0; + if (p_mode != ProcessMode::PROCESS_CACHE) { + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + } else { + l.char_count += 1; //images count as chars too + } + + ItemImage *img = static_cast(it); + + Ref font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_char) { + *r_click_char = 0; + } + + if (_ensure_width(img->size.width)) { + return nonblank_line_count; + } + + bool visible = visible_characters < 0 || (p_char_count < visible_characters && _y_range_visible(y + lh - font->get_descent() - img->size.height, img->size.height)); + if (visible) { + line_is_blank = false; + } + + if (p_mode == ProcessMode::PROCESS_DRAW && visible) { + img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size)); + } + p_char_count++; + + if (_advance(img->size.width)) { + return nonblank_line_count; + } + + _check_height(img->size.height + font->get_descent()); + + } break; + case ItemType::ITEM_NEWLINE: { + lh = 0; + + if (p_mode != ProcessMode::PROCESS_CACHE) { + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_is_blank = true; + } + + } break; + case ItemType::ITEM_TABLE: { + lh = 0; + ItemTable *table = static_cast(it); + int hseparation = get_theme_constant("table_hseparation"); + int vseparation = get_theme_constant("table_vseparation"); + Color ccolor = _find_color(table, p_base_color); + Vector2 draw_ofs = Point2(wofs, y); + Color font_color_shadow = get_theme_color("font_color_shadow"); + bool use_outline = get_theme_constant("shadow_as_outline"); + Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); + + if (p_mode == ProcessMode::PROCESS_CACHE) { + int idx = 0; + //set minimums to zero + for (int i = 0; i < table->columns.size(); i++) { + table->columns.write[i].min_width = 0; + table->columns.write[i].max_width = 0; + table->columns.write[i].width = 0; + } + //compute minimum width for each cell + const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; + + for (List::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames + ItemFrame *frame = static_cast(E->get()); + + int column = idx % table->columns.size(); + + int ly = 0; + + for (int i = 0; i < frame->lines.size(); i++) { + _process_line(frame, Point2(), ly, available_width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); + table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); + } + idx++; + } + + //compute available width and total ratio (for expanders) + + int total_ratio = 0; + int remaining_width = available_width; + table->total_width = hseparation; + + for (int i = 0; i < table->columns.size(); i++) { + remaining_width -= table->columns[i].min_width; + if (table->columns[i].max_width > table->columns[i].min_width) { + table->columns.write[i].expand = true; + } + if (table->columns[i].expand) { + total_ratio += table->columns[i].expand_ratio; + } + } + + //assign actual widths + for (int i = 0; i < table->columns.size(); i++) { + table->columns.write[i].width = table->columns[i].min_width; + if (table->columns[i].expand && total_ratio > 0) { + table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + } + table->total_width += table->columns[i].width + hseparation; + } + + //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 < table->columns.size(); 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.write[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 < table->columns.size(); 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.write[i].width += incr; + table->total_width += incr; + } + } + } + } + } + + //compute caches properly again with the right width + idx = 0; + for (List::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames + ItemFrame *frame = static_cast(E->get()); + + int column = idx % table->columns.size(); + + for (int i = 0; i < frame->lines.size(); i++) { + int ly = 0; + _process_line(frame, Point2(), ly, table->columns[column].width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + frame->lines.write[i].height_cache = ly; //actual height + frame->lines.write[i].height_accum_cache = ly; //actual height + } + idx++; + } + } + + Point2 offset(align_ofs + hseparation, vseparation); + + int row_height = 0; + //draw using computed caches + int idx = 0; + for (List::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames + ItemFrame *frame = static_cast(E->get()); + + int column = idx % table->columns.size(); + + int ly = 0; + int yofs = 0; + + int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache); + int lines_ofs = p_ofs.y + offset.y + draw_ofs.y; + + bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0; + if (visible) { + line_is_blank = false; + } + + for (int i = 0; i < frame->lines.size(); i++) { + if (visible) { + if (p_mode == ProcessMode::PROCESS_DRAW) { + nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); + } else if (p_mode == ProcessMode::PROCESS_POINTER) { + _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); + if (r_click_item && *r_click_item) { + return nonblank_line_count; // exit early + } + } + } + + yofs += frame->lines[i].height_cache; + if (p_mode == ProcessMode::PROCESS_CACHE) { + frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; + } + } + + row_height = MAX(yofs, row_height); + offset.x += table->columns[column].width + hseparation; + + if (column == table->columns.size() - 1) { + offset.y += row_height + vseparation; + offset.x = hseparation; + row_height = 0; + } + idx++; + } + + int total_height = offset.y; + if (row_height) { + total_height = row_height + vseparation; + } + if (_advance(table->total_width)) { + return nonblank_line_count; + } + _check_height(total_height); + + } break; + + default: { + } + } + + Item *itp = it; + + it = _get_next_item(it); + + if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { + //went to next line, but pointer was on the previous one + if (r_outside) { + *r_outside = true; + } + *r_click_item = itp; + *r_click_char = rchar; + return nonblank_line_count; + } + + break; + } + } + _new_line(); + + return nonblank_line_count; +} + +void BbCodeParser::start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, const int p_line_height, const int _p_width, const int _p_line, const Ref &p_base_font) { + p_frame = _p_frame; + p_ofs = _p_ofs; + p_height = p_line_height; + p_width = _p_width; + p_line = _p_line; + + l = p_frame->lines.write[p_line]; + it = l.from; + + margin = _find_margin(it, p_base_font); + align = _find_align(it); + height = get_size().y; + + wofs = margin; + + /*if (p_mode != ProcessMode::PROCESS_CACHE && align != Align::ALIGN_FILL) { + wofs += line_ofs; + }*/ + + begin = wofs; + + cfont = _find_font(it); + if (cfont.is_null()) { + cfont = p_base_font; + } + + //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed + line_height = cfont->get_height(); + line_ascent = cfont->get_ascent(); + line_descent = cfont->get_descent(); +} + +void BbCodeParser::process_cache() { + ERR_FAIL_INDEX(line, l.offset_caches.size()); + line_ofs = l.offset_caches[line]; + + l.offset_caches.clear(); + l.height_caches.clear(); + l.ascent_caches.clear(); + l.descent_caches.clear(); + l.char_count = 0; + l.minimum_width = 0; + l.maximum_width = 0; +} + +void BbCodeParser::process_draw() { +} diff --git a/scene/gui/bbcode.h b/scene/gui/bbcode.h new file mode 100644 index 000000000000..eb5e9747de89 --- /dev/null +++ b/scene/gui/bbcode.h @@ -0,0 +1,131 @@ + +#include "core/math/vector2.h" +#include "rich_text_effect.h" +#include "scene/gui/rich_text_label.h" + +class BbCodeParser : Control { + GDCLASS(BbCodeParser, Control); + //We use the classes from RTL here, to have a quick dependency overview + //Later, we should move them over here, if we no longer have dependencies in the RTL + using Line = RichTextLabel::Line; + using Item = RichTextLabel::Item; + using ItemFrame = RichTextLabel::ItemFrame; + using ProcessMode = RichTextLabel::ProcessMode; + using Align = RichTextLabel::Align; + using ItemType = RichTextLabel::ItemType; + using ItemAlign = RichTextLabel::ItemAlign; + using ItemIndent = RichTextLabel::ItemIndent; + using ItemFont = RichTextLabel::ItemFont; + using ItemText = RichTextLabel::ItemText; + using ItemFade = RichTextLabel::ItemFade; + using ItemFX = RichTextLabel::ItemFX; + using ItemMeta = RichTextLabel::ItemMeta; + using ItemColor = RichTextLabel::ItemColor; + using Selection = RichTextLabel::Selection; + using ItemCustomFX = RichTextLabel::ItemCustomFX; + using ItemShake = RichTextLabel::ItemShake; + using ItemWave = RichTextLabel::ItemWave; + using ItemTornado = RichTextLabel::ItemTornado; + using ItemRainbow = RichTextLabel::ItemRainbow; + using ItemImage = RichTextLabel::ItemImage; + using ItemTable = RichTextLabel::ItemTable; + +private: + //localy declared vars in _process_line + Line &l = Line(); + Item *it = nullptr; + int line_ofs = 0; + int margin = 0; + Align align = Align::ALIGN_LEFT; + int line = 0; + int spaces = 0; + int height = 0; + int wofs = 0; + int spaces_size = 0; + int align_ofs = 0; + int begin = 0; + Ref cfont; + + int line_height = 0; + int line_ascent = 0; + int line_descent = 0; + + int backtrack = 0; // for dynamic hidden content. + int nonblank_line_count = 0; //number of nonblank lines as counted during PROCESS_DRAW + Variant meta; + + Color selection_fg; + Color selection_bg; + + int rchar = 0; + int lh = 0; + bool line_is_blank = true; + bool line_wrapped = false; + int fh = 0; + +private: + //Makros as functions + bool _new_line(); + bool _ensure_width(int m_width); + bool _advance(int m_width); //rename? + void _check_height(int m_height); //rename? + bool _y_range_visible(int m_top, int m_height); + +private: + //Copied functions + Item *_get_next_item(Item *p_item, bool p_free = false) const; + Ref _find_font(Item *p_item); + int _find_margin(Item *p_item, const Ref &p_base_font); + Align _find_align(Item *p_item); + void _fetch_item_fx_stack(Item *p_item, Vector &r_stack); + Color _find_color(Item *p_item, const Color &p_default_color); + bool _find_underline(Item *p_item); + bool _find_strikethrough(Item *p_item); + bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); + + int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); + + //TODOS: + // split process_modes into different functions + // get rid of the Process Modes + +public: + void start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, const int _p_height, const int _p_width, const int _p_line, const Ref &p_base_font); + void process(); + void process_cache(); + void process_draw(); + void process_pointer(); + + int get_data(); + +public: + //Duplicated vars + int tab_size = 4; + RichTextLabel::Align default_align = RichTextLabel::Align::ALIGN_LEFT; + bool underline_meta = false; + bool override_selected_font_color = false; + Selection selection; + int visible_characters = -1; + +public: + //LineParser arguments + ItemFrame *p_frame = nullptr; + Vector2 &p_ofs = Vector2(); + int p_height = 0; //is altered ONLY when a new line is added //was y + int p_width = 0; + int p_line = 0; + RichTextLabel::ProcessMode p_mode = ProcessMode::PROCESS_DRAW; + Ref &p_base_font = Ref(); + Color &p_base_color = Color(); + Color &p_font_color_shadow = Color(); + bool p_shadow_as_outline = false; + Point2 &shadow_ofs = Point2(); + Point2i &p_click_pos = Point2i(); + RichTextLabel::Item **r_click_item = nullptr; + int *r_click_char = nullptr; + bool *r_outside = nullptr; + int p_char_count = 0; + + BbCodeParser(); + ~BbCodeParser(); +}; From ab62fda53af8f79976401175c26bf8a3a1a8460b Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Wed, 3 Jun 2020 18:59:43 +0200 Subject: [PATCH 4/9] Abstract all big chunks of _process_line into functions --- scene/gui/bbcode.cpp | 1095 +++++++++++++++++++++--------------------- scene/gui/bbcode.h | 51 +- 2 files changed, 606 insertions(+), 540 deletions(-) diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp index 795283a8c8cc..0efc4140e8ac 100644 --- a/scene/gui/bbcode.cpp +++ b/scene/gui/bbcode.cpp @@ -10,10 +10,15 @@ #include "editor/editor_scale.h" #endif -BbCodeParser::BbCodeParser() {} +BbCodeParser::BbCodeParser() { + _process_line(p_frame, p_ofs, p_height, p_width, p_line, ProcessMode::PROCESS_CACHE, p_base_font, p_base_color, p_base_color, p_shadow_as_outline, shadow_ofs); +} BbCodeParser::~BbCodeParser() {} +void BbCodeParser::_bind_methods() { +} + bool BbCodeParser::_new_line() { if (p_mode != ProcessMode::PROCESS_CACHE) { line++; @@ -115,7 +120,520 @@ void BbCodeParser::_check_height(int m_height) { } bool BbCodeParser::_y_range_visible(int m_top, int m_height) { - return (m_height > 0 && ((m_top >= 0 && m_top < height) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < height))); + return (m_height > 0 && ((m_top >= 0 && m_top < get_size().y) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < get_size().y))); +} + +bool BbCodeParser::_parse_text(ItemText *text) { + Ref font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + + const CharType *c = text->text.c_str(); + const CharType *cf = c; + int ascent = font->get_ascent(); + int descent = font->get_descent(); + + Color color; + Color font_color_shadow; + bool underline = false; + bool strikethrough = false; + ItemFade *fade = nullptr; + int it_char_start = p_char_count; + + Vector fx_stack = Vector(); + _fetch_item_fx_stack(text, fx_stack); + bool custom_fx_ok = true; + + if (p_mode == ProcessMode::PROCESS_DRAW) { + color = _find_color(text, p_base_color); + font_color_shadow = _find_color(text, p_font_color_shadow); + if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { + underline = true; + } else if (_find_strikethrough(text)) { + strikethrough = true; + } + + Item *fade_item = it; + while (fade_item) { + if (fade_item->type == ItemType::ITEM_FADE) { + fade = static_cast(fade_item); + break; + } + fade_item = fade_item->parent; + } + + } else if (p_mode == ProcessMode::PROCESS_CACHE) { + l.char_count += text->text.length(); + } + + rchar = 0; + FontDrawer drawer(font, Color(1, 1, 1)); + while (*c) { + int end = 0; + int w = 0; + int fw = 0; + + lh = 0; + + if (p_mode != ProcessMode::PROCESS_CACHE) { + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; + line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; + } + while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) { + int cw = font->get_char_size(c[end], c[end + 1]).width; + if (c[end] == '\t') { + cw = tab_size * font->get_char_size(' ').width; + } + + if (end > 0 && fw + cw + begin > p_width) { + break; //don't allow lines longer than assigned width + } + + fw += cw; + + end++; + } + _check_height(fh); + if (_ensure_width(fw)) { + return true; + } + + line_ascent = MAX(line_ascent, ascent); + line_descent = MAX(line_descent, descent); + fh = line_ascent + line_descent; + + if (end && c[end - 1] == ' ') { + if (p_mode == ProcessMode::PROCESS_CACHE) { + spaces_size += font->get_char_size(' ').width; + } else if (align == Align::ALIGN_FILL) { + int ln = MIN(l.offset_caches.size() - 1, line); + if (l.space_caches[ln]) { + align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln]; + } + } + spaces++; + } + + { + int ofs = 0 - backtrack; + + for (int i = 0; i < end; i++) { + int pofs = wofs + ofs; + + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh) { + int cw = font->get_char_size(c[i], c[i + 1]).x; + + if (c[i] == '\t') { + cw = tab_size * font->get_char_size(' ').width; + } + + if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) { + rchar = int((&c[i]) - cf); + } + + ofs += cw; + } else if (p_mode == ProcessMode::PROCESS_DRAW) { + bool selected = false; + Color fx_color = Color(color); + Point2 fx_offset; + CharType fx_char = c[i]; + + if (selection.active) { + int cofs = (&c[i]) - cf; + if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) { + selected = true; + } + } + + int cw = 0; + int c_item_offset = p_char_count - it_char_start; + + float faded_visibility = 1.0f; + if (fade) { + if (c_item_offset >= fade->starting_index) { + faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; + faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; + } + fx_color.a = faded_visibility; + } + + bool visible = visible_characters < 0 || + ((p_char_count < visible_characters && + _y_range_visible(p_height + lh - line_descent - line_ascent, line_ascent + line_descent)) && + faded_visibility > 0.0f); + + const bool previously_visible = visible; + + for (int j = 0; j < fx_stack.size(); j++) { + ItemFX *item_fx = fx_stack[j]; + + if (item_fx->type == ItemType::ITEM_CUSTOMFX && custom_fx_ok) { + ItemCustomFX *item_custom = static_cast(item_fx); + + Ref charfx = item_custom->char_fx_transform; + Ref custom_effect = item_custom->custom_effect; + + if (!custom_effect.is_null()) { + charfx->elapsed_time = item_custom->elapsed_time; + charfx->relative_index = c_item_offset; + charfx->absolute_index = p_char_count; + charfx->visibility = visible; + charfx->offset = fx_offset; + charfx->color = fx_color; + charfx->character = fx_char; + + bool effect_status = custom_effect->_process_effect_impl(charfx); + custom_fx_ok = effect_status; + + fx_offset += charfx->offset; + fx_color = charfx->color; + visible &= charfx->visibility; + fx_char = charfx->character; + } + } else if (item_fx->type == ItemType::ITEM_SHAKE) { + ItemShake *item_shake = static_cast(item_fx); + + uint64_t char_current_rand = item_shake->offset_random(c_item_offset); + uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + fx_offset += Point2(Math::lerp(Math::sin(previous_offset), + Math::sin(current_offset), + n_time), + Math::lerp(Math::cos(previous_offset), + Math::cos(current_offset), + n_time)) * + (float)item_shake->strength / 10.0f; + } else if (item_fx->type == ItemType::ITEM_WAVE) { + ItemWave *item_wave = static_cast(item_fx); + + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); + fx_offset += Point2(0, 1) * value; + } else if (item_fx->type == ItemType::ITEM_TORNADO) { + ItemTornado *item_tornado = static_cast(item_fx); + + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + fx_offset += Point2(torn_x, torn_y); + } else if (item_fx->type == ItemType::ITEM_RAINBOW) { + ItemRainbow *item_rainbow = static_cast(item_fx); + + fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), + item_rainbow->saturation, + item_rainbow->value, + fx_color.a); + } + } + + if (visible) { + line_is_blank = false; + w += font->get_char_size(c[i], c[i + 1]).x; + } + + if (c[i] == '\t') { + visible = false; + } + + if (visible) { + if (selected) { + cw = font->get_char_size(fx_char, c[i + 1]).x; + draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + p_height, cw, lh), selection_bg); + } + + if (p_font_color_shadow.a > 0) { + float x_ofs_shadow = align_ofs + pofs; + float y_ofs_shadow = p_height + lh - line_descent; + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + + if (p_shadow_as_outline) { + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + } + } + + if (selected) { + drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, p_height + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); + } else { + cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, p_height + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); + } + } else if (previously_visible && c[i] != '\t') { + backtrack += font->get_char_size(fx_char, c[i + 1]).x; + } + + p_char_count++; + if (c[i] == '\t') { + cw = tab_size * font->get_char_size(' ').width; + backtrack = MAX(0, backtrack - cw); + } + + ofs += cw; + } + } + + if (underline) { + Color uc = color; + uc.a *= 0.5; + int uy = p_height + lh - line_descent + font->get_underline_position(); + float underline_width = font->get_underline_thickness(); +#ifdef TOOLS_ENABLED + underline_width *= EDSCALE; +#endif + RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); + } else if (strikethrough) { + Color uc = color; + uc.a *= 0.5; + int uy = p_height + lh - (line_ascent + line_descent) / 2; + float strikethrough_width = font->get_underline_thickness(); +#ifdef TOOLS_ENABLED + strikethrough_width *= EDSCALE; +#endif + RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width); + } + } + + if (_advance(fw)) { + return nonblank_line_count; + } + _check_height(fh); //must be done somewhere + c = &c[end]; + } + + return false; +} + +bool BbCodeParser::_parse_table(ItemTable *table) { + lh = 0; + int hseparation = get_theme_constant("table_hseparation"); + int vseparation = get_theme_constant("table_vseparation"); + Color ccolor = _find_color(table, p_base_color); + Vector2 draw_ofs = Point2(wofs, p_height); + Color font_color_shadow = get_theme_color("font_color_shadow"); + bool use_outline = get_theme_constant("shadow_as_outline"); + Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); + + if (p_mode == ProcessMode::PROCESS_CACHE) { + int idx = 0; + //set minimums to zero + for (int i = 0; i < table->columns.size(); i++) { + table->columns.write[i].min_width = 0; + table->columns.write[i].max_width = 0; + table->columns.write[i].width = 0; + } + //compute minimum width for each cell + const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; + + for (List::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames + ItemFrame *frame = static_cast(E->get()); + + int column = idx % table->columns.size(); + + int ly = 0; + + for (int i = 0; i < frame->lines.size(); i++) { + _process_line(frame, Point2(), ly, available_width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); + table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); + } + idx++; + } + + //compute available width and total ratio (for expanders) + + int total_ratio = 0; + int remaining_width = available_width; + table->total_width = hseparation; + + for (int i = 0; i < table->columns.size(); i++) { + remaining_width -= table->columns[i].min_width; + if (table->columns[i].max_width > table->columns[i].min_width) { + table->columns.write[i].expand = true; + } + if (table->columns[i].expand) { + total_ratio += table->columns[i].expand_ratio; + } + } + + //assign actual widths + for (int i = 0; i < table->columns.size(); i++) { + table->columns.write[i].width = table->columns[i].min_width; + if (table->columns[i].expand && total_ratio > 0) { + table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + } + table->total_width += table->columns[i].width + hseparation; + } + + //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 < table->columns.size(); 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.write[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 < table->columns.size(); 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.write[i].width += incr; + table->total_width += incr; + } + } + } + } + } + + //compute caches properly again with the right width + idx = 0; + for (List::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames + ItemFrame *frame = static_cast(E->get()); + + int column = idx % table->columns.size(); + + for (int i = 0; i < frame->lines.size(); i++) { + int ly = 0; + _process_line(frame, Point2(), ly, table->columns[column].width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + frame->lines.write[i].height_cache = ly; //actual height + frame->lines.write[i].height_accum_cache = ly; //actual height + } + idx++; + } + } + + Point2 offset(align_ofs + hseparation, vseparation); + + int row_height = 0; + //draw using computed caches + int idx = 0; + for (List::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames + ItemFrame *frame = static_cast(E->get()); + + int column = idx % table->columns.size(); + + int ly = 0; + int yofs = 0; + + int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache); + int lines_ofs = p_ofs.y + offset.y + draw_ofs.y; + + bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0; + if (visible) { + line_is_blank = false; + } + + for (int i = 0; i < frame->lines.size(); i++) { + if (visible) { + if (p_mode == ProcessMode::PROCESS_DRAW) { + nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); + } else if (p_mode == ProcessMode::PROCESS_POINTER) { + _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); + if (r_click_item && *r_click_item) { + return nonblank_line_count; // exit early + } + } + } + + yofs += frame->lines[i].height_cache; + if (p_mode == ProcessMode::PROCESS_CACHE) { + frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; + } + } + + row_height = MAX(yofs, row_height); + offset.x += table->columns[column].width + hseparation; + + if (column == table->columns.size() - 1) { + offset.y += row_height + vseparation; + offset.x = hseparation; + row_height = 0; + } + idx++; + } + + int total_height = offset.y; + if (row_height) { + total_height = row_height + vseparation; + } + if (_advance(table->total_width)) { + return nonblank_line_count; + } + _check_height(total_height); + return false; +} + +bool BbCodeParser::_parse_image(ItemImage *img) { + lh = 0; + if (p_mode != ProcessMode::PROCESS_CACHE) { + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + } else { + l.char_count += 1; //images count as chars too + } + + Ref font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_char) { + *r_click_char = 0; + } + + if (_ensure_width(img->size.width)) { + return nonblank_line_count; + } + + bool visible = visible_characters < 0 || (p_char_count < visible_characters && _y_range_visible(p_height + lh - font->get_descent() - img->size.height, img->size.height)); + if (visible) { + line_is_blank = false; + } + + if (p_mode == ProcessMode::PROCESS_DRAW && visible) { + img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, p_height + lh - font->get_descent() - img->size.height), img->size)); + } + p_char_count++; + + if (_advance(img->size.width)) { + return nonblank_line_count; + } + + _check_height(img->size.height + font->get_descent()); + + return false; +} + +bool BbCodeParser::_parse_detect_click(Item *previous_item) { + if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh) { + //went to next line, but pointer was on the previous one + if (r_outside) { + *r_outside = true; + } + *r_click_item = previous_item; + *r_click_char = rchar; + return true; + } + return false; } BbCodeParser::Item *BbCodeParser::_get_next_item(Item *p_item, bool p_free) const { @@ -300,7 +818,6 @@ void BbCodeParser::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { ERR_FAIL_INDEX_V((int)p_mode, 3, 0); - RID ci; if (r_outside) { *r_outside = false; } @@ -311,16 +828,14 @@ int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y *r_click_item = nullptr; } } - Line &l = p_frame->lines.write[p_line]; - Item *it = l.from; - - int line_ofs = 0; - int margin = _find_margin(it, p_base_font); - RichTextLabel::Align align = _find_align(it); - int line = 0; - int spaces = 0; + l = p_frame->lines.write[p_line]; + it = l.from; - int height = get_size().y; + line_ofs = 0; + margin = _find_margin(it, p_base_font); + align = _find_align(it); + line = 0; + spaces = 0; if (p_mode != ProcessMode::PROCESS_CACHE) { ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0); @@ -337,386 +852,66 @@ int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y l.maximum_width = 0; } - int wofs = margin; - int spaces_size = 0; - int align_ofs = 0; + wofs = margin; + spaces_size = 0; + align_ofs = 0; if (p_mode != ProcessMode::PROCESS_CACHE && align != Align::ALIGN_FILL) { wofs += line_ofs; } - int begin = wofs; + begin = wofs; - Ref cfont = _find_font(it); + cfont = _find_font(it); if (cfont.is_null()) { cfont = p_base_font; } //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed - int line_height = cfont->get_height(); - int line_ascent = cfont->get_ascent(); - int line_descent = cfont->get_descent(); - - int backtrack = 0; // for dynamic hidden content. - - int nonblank_line_count = 0; //number of nonblank lines as counted during ProcessMode::PROCESS_DRAW + line_height = cfont->get_height(); + line_ascent = cfont->get_ascent(); + line_descent = cfont->get_descent(); - Variant meta; + backtrack = 0; // for dynamic hidden content. - Color selection_fg; - Color selection_bg; + nonblank_line_count = 0; //number of nonblank lines as counted during ProcessMode::PROCESS_DRAW if (p_mode == RichTextLabel::ProcessMode::PROCESS_DRAW) { selection_fg = get_theme_color("font_color_selected"); selection_bg = get_theme_color("selection_color"); } - int rchar = 0; - int lh = 0; - bool line_is_blank = true; - bool line_wrapped = false; - int fh = 0; + rchar = 0; + lh = 0; + line_is_blank = true; + line_wrapped = false; + fh = 0; while (it) { switch (it->type) { case ItemType::ITEM_ALIGN: { ItemAlign *align_it = static_cast(it); - align = align_it->align; - } break; case ItemType::ITEM_INDENT: { - if (it != l.from) { - ItemIndent *indent_it = static_cast(it); - - int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; - margin += indent; - begin += indent; - wofs += indent; + if (it == l.from) { + break; } - + ItemIndent *indent_it = static_cast(it); + int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; + margin += indent; + begin += indent; + wofs += indent; } break; case ItemType::ITEM_TEXT: { - ItemText *text = static_cast(it); - - Ref font = _find_font(it); - if (font.is_null()) { - font = p_base_font; - } - - const CharType *c = text->text.c_str(); - const CharType *cf = c; - int ascent = font->get_ascent(); - int descent = font->get_descent(); - - Color color; - Color font_color_shadow; - bool underline = false; - bool strikethrough = false; - ItemFade *fade = nullptr; - int it_char_start = p_char_count; - - Vector fx_stack = Vector(); - _fetch_item_fx_stack(text, fx_stack); - bool custom_fx_ok = true; - - if (p_mode == ProcessMode::PROCESS_DRAW) { - color = _find_color(text, p_base_color); - font_color_shadow = _find_color(text, p_font_color_shadow); - if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { - underline = true; - } else if (_find_strikethrough(text)) { - strikethrough = true; - } - - Item *fade_item = it; - while (fade_item) { - if (fade_item->type == ItemType::ITEM_FADE) { - fade = static_cast(fade_item); - break; - } - fade_item = fade_item->parent; - } - - } else if (p_mode == ProcessMode::PROCESS_CACHE) { - l.char_count += text->text.length(); - } - - rchar = 0; - FontDrawer drawer(font, Color(1, 1, 1)); - while (*c) { - int end = 0; - int w = 0; - int fw = 0; - - lh = 0; - - if (p_mode != ProcessMode::PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; - line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; - } - while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) { - int cw = font->get_char_size(c[end], c[end + 1]).width; - if (c[end] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - } - - if (end > 0 && fw + cw + begin > p_width) { - break; //don't allow lines longer than assigned width - } - - fw += cw; - - end++; - } - _check_height(fh); - if (_ensure_width(fw)) { - return nonblank_line_count; - } - - line_ascent = MAX(line_ascent, ascent); - line_descent = MAX(line_descent, descent); - fh = line_ascent + line_descent; - - if (end && c[end - 1] == ' ') { - if (p_mode == ProcessMode::PROCESS_CACHE) { - spaces_size += font->get_char_size(' ').width; - } else if (align == Align::ALIGN_FILL) { - int ln = MIN(l.offset_caches.size() - 1, line); - if (l.space_caches[ln]) { - align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln]; - } - } - spaces++; - } - - { - int ofs = 0 - backtrack; - - for (int i = 0; i < end; i++) { - int pofs = wofs + ofs; - - if (p_mode == ProcessMode::PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { - int cw = font->get_char_size(c[i], c[i + 1]).x; - - if (c[i] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - } - - if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) { - rchar = int((&c[i]) - cf); - } - - ofs += cw; - } else if (p_mode == ProcessMode::PROCESS_DRAW) { - bool selected = false; - Color fx_color = Color(color); - Point2 fx_offset; - CharType fx_char = c[i]; - - if (selection.active) { - int cofs = (&c[i]) - cf; - if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) { - selected = true; - } - } - - int cw = 0; - int c_item_offset = p_char_count - it_char_start; - - float faded_visibility = 1.0f; - if (fade) { - if (c_item_offset >= fade->starting_index) { - faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; - faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; - } - fx_color.a = faded_visibility; - } - - bool visible = visible_characters < 0 || ((p_char_count < visible_characters && _y_range_visible(y + lh - line_descent - line_ascent, line_ascent + line_descent)) && - faded_visibility > 0.0f); - - const bool previously_visible = visible; - - for (int j = 0; j < fx_stack.size(); j++) { - ItemFX *item_fx = fx_stack[j]; - - if (item_fx->type == ItemType::ITEM_CUSTOMFX && custom_fx_ok) { - ItemCustomFX *item_custom = static_cast(item_fx); - - Ref charfx = item_custom->char_fx_transform; - Ref custom_effect = item_custom->custom_effect; - - if (!custom_effect.is_null()) { - charfx->elapsed_time = item_custom->elapsed_time; - charfx->relative_index = c_item_offset; - charfx->absolute_index = p_char_count; - charfx->visibility = visible; - charfx->offset = fx_offset; - charfx->color = fx_color; - charfx->character = fx_char; - - bool effect_status = custom_effect->_process_effect_impl(charfx); - custom_fx_ok = effect_status; - - fx_offset += charfx->offset; - fx_color = charfx->color; - visible &= charfx->visibility; - fx_char = charfx->character; - } - } else if (item_fx->type == ItemType::ITEM_SHAKE) { - ItemShake *item_shake = static_cast(item_fx); - - uint64_t char_current_rand = item_shake->offset_random(c_item_offset); - uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); - uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); - n_time = (n_time > 1.0) ? 1.0 : n_time; - fx_offset += Point2(Math::lerp(Math::sin(previous_offset), - Math::sin(current_offset), - n_time), - Math::lerp(Math::cos(previous_offset), - Math::cos(current_offset), - n_time)) * - (float)item_shake->strength / 10.0f; - } else if (item_fx->type == ItemType::ITEM_WAVE) { - ItemWave *item_wave = static_cast(item_fx); - - double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); - fx_offset += Point2(0, 1) * value; - } else if (item_fx->type == ItemType::ITEM_TORNADO) { - ItemTornado *item_tornado = static_cast(item_fx); - - double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); - double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); - fx_offset += Point2(torn_x, torn_y); - } else if (item_fx->type == ItemType::ITEM_RAINBOW) { - ItemRainbow *item_rainbow = static_cast(item_fx); - - fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), - item_rainbow->saturation, - item_rainbow->value, - fx_color.a); - } - } - - if (visible) { - line_is_blank = false; - w += font->get_char_size(c[i], c[i + 1]).x; - } - - if (c[i] == '\t') { - visible = false; - } - - if (visible) { - if (selected) { - cw = font->get_char_size(fx_char, c[i + 1]).x; - draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); - } - - if (p_font_color_shadow.a > 0) { - float x_ofs_shadow = align_ofs + pofs; - float y_ofs_shadow = y + lh - line_descent; - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - - if (p_shadow_as_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - } - } - - if (selected) { - drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); - } else { - cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); - } - } else if (previously_visible && c[i] != '\t') { - backtrack += font->get_char_size(fx_char, c[i + 1]).x; - } - - p_char_count++; - if (c[i] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - backtrack = MAX(0, backtrack - cw); - } - - ofs += cw; - } - } - - if (underline) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - line_descent + font->get_underline_position(); - float underline_width = font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - underline_width *= EDSCALE; -#endif - RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); - } else if (strikethrough) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - (line_ascent + line_descent) / 2; - float strikethrough_width = font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - strikethrough_width *= EDSCALE; -#endif - RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width); - } - } - - if (_advance(fw)) { - return nonblank_line_count; - } - _check_height(fh); //must be done somewhere - c = &c[end]; + if (_parse_text(static_cast(it))) { + return nonblank_line_count; } - } break; case ItemType::ITEM_IMAGE: { - lh = 0; - if (p_mode != ProcessMode::PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - } else { - l.char_count += 1; //images count as chars too - } - - ItemImage *img = static_cast(it); - - Ref font = _find_font(it); - if (font.is_null()) { - font = p_base_font; - } - - if (p_mode == ProcessMode::PROCESS_POINTER && r_click_char) { - *r_click_char = 0; - } - - if (_ensure_width(img->size.width)) { + if (_parse_image(static_cast(it))) { return nonblank_line_count; } - - bool visible = visible_characters < 0 || (p_char_count < visible_characters && _y_range_visible(y + lh - font->get_descent() - img->size.height, img->size.height)); - if (visible) { - line_is_blank = false; - } - - if (p_mode == ProcessMode::PROCESS_DRAW && visible) { - img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size)); - } - p_char_count++; - - if (_advance(img->size.width)) { - return nonblank_line_count; - } - - _check_height(img->size.height + font->get_descent()); - } break; case ItemType::ITEM_NEWLINE: { lh = 0; @@ -728,179 +923,9 @@ int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y } break; case ItemType::ITEM_TABLE: { - lh = 0; - ItemTable *table = static_cast(it); - int hseparation = get_theme_constant("table_hseparation"); - int vseparation = get_theme_constant("table_vseparation"); - Color ccolor = _find_color(table, p_base_color); - Vector2 draw_ofs = Point2(wofs, y); - Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); - Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); - - if (p_mode == ProcessMode::PROCESS_CACHE) { - int idx = 0; - //set minimums to zero - for (int i = 0; i < table->columns.size(); i++) { - table->columns.write[i].min_width = 0; - table->columns.write[i].max_width = 0; - table->columns.write[i].width = 0; - } - //compute minimum width for each cell - const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; - - for (List::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames - ItemFrame *frame = static_cast(E->get()); - - int column = idx % table->columns.size(); - - int ly = 0; - - for (int i = 0; i < frame->lines.size(); i++) { - _process_line(frame, Point2(), ly, available_width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); - table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); - table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); - } - idx++; - } - - //compute available width and total ratio (for expanders) - - int total_ratio = 0; - int remaining_width = available_width; - table->total_width = hseparation; - - for (int i = 0; i < table->columns.size(); i++) { - remaining_width -= table->columns[i].min_width; - if (table->columns[i].max_width > table->columns[i].min_width) { - table->columns.write[i].expand = true; - } - if (table->columns[i].expand) { - total_ratio += table->columns[i].expand_ratio; - } - } - - //assign actual widths - for (int i = 0; i < table->columns.size(); i++) { - table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { - table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; - } - table->total_width += table->columns[i].width + hseparation; - } - - //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 < table->columns.size(); 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.write[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 < table->columns.size(); 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.write[i].width += incr; - table->total_width += incr; - } - } - } - } - } - - //compute caches properly again with the right width - idx = 0; - for (List::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames - ItemFrame *frame = static_cast(E->get()); - - int column = idx % table->columns.size(); - - for (int i = 0; i < frame->lines.size(); i++) { - int ly = 0; - _process_line(frame, Point2(), ly, table->columns[column].width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); - frame->lines.write[i].height_cache = ly; //actual height - frame->lines.write[i].height_accum_cache = ly; //actual height - } - idx++; - } - } - - Point2 offset(align_ofs + hseparation, vseparation); - - int row_height = 0; - //draw using computed caches - int idx = 0; - for (List::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ItemType::ITEM_FRAME); //children should all be frames - ItemFrame *frame = static_cast(E->get()); - - int column = idx % table->columns.size(); - - int ly = 0; - int yofs = 0; - - int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache); - int lines_ofs = p_ofs.y + offset.y + draw_ofs.y; - - bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0; - if (visible) { - line_is_blank = false; - } - - for (int i = 0; i < frame->lines.size(); i++) { - if (visible) { - if (p_mode == ProcessMode::PROCESS_DRAW) { - nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); - } else if (p_mode == ProcessMode::PROCESS_POINTER) { - _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); - if (r_click_item && *r_click_item) { - return nonblank_line_count; // exit early - } - } - } - - yofs += frame->lines[i].height_cache; - if (p_mode == ProcessMode::PROCESS_CACHE) { - frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; - } - } - - row_height = MAX(yofs, row_height); - offset.x += table->columns[column].width + hseparation; - - if (column == table->columns.size() - 1) { - offset.y += row_height + vseparation; - offset.x = hseparation; - row_height = 0; - } - idx++; - } - - int total_height = offset.y; - if (row_height) { - total_height = row_height + vseparation; - } - if (_advance(table->total_width)) { + if (_parse_table(static_cast(it))) { return nonblank_line_count; } - _check_height(total_height); } break; @@ -913,20 +938,17 @@ int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y it = _get_next_item(it); if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { - if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { - //went to next line, but pointer was on the previous one - if (r_outside) { - *r_outside = true; - } - *r_click_item = itp; - *r_click_char = rchar; + if (_parse_detect_click(itp)) { return nonblank_line_count; } break; } } - _new_line(); + if (_new_line()) { + //Safe is safe. If somebody does something below _new_line we have a bug + return nonblank_line_count; + } return nonblank_line_count; } @@ -943,7 +965,6 @@ void BbCodeParser::start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, con margin = _find_margin(it, p_base_font); align = _find_align(it); - height = get_size().y; wofs = margin; diff --git a/scene/gui/bbcode.h b/scene/gui/bbcode.h index eb5e9747de89..700bd6198152 100644 --- a/scene/gui/bbcode.h +++ b/scene/gui/bbcode.h @@ -1,10 +1,42 @@ +/*************************************************************************/ +/* panel.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef BBCODE_H +#define BBCODE_H #include "core/math/vector2.h" #include "rich_text_effect.h" #include "scene/gui/rich_text_label.h" -class BbCodeParser : Control { - GDCLASS(BbCodeParser, Control); +class BbCodeParser : Label { + GDCLASS(BbCodeParser, Label); //We use the classes from RTL here, to have a quick dependency overview //Later, we should move them over here, if we no longer have dependencies in the RTL using Line = RichTextLabel::Line; @@ -30,16 +62,20 @@ class BbCodeParser : Control { using ItemImage = RichTextLabel::ItemImage; using ItemTable = RichTextLabel::ItemTable; +protected: + static void _bind_methods(); + private: //localy declared vars in _process_line + //Actual line processed by process line (determined using p_frame and p_line) Line &l = Line(); Item *it = nullptr; + RID ci = get_canvas_item(); int line_ofs = 0; int margin = 0; Align align = Align::ALIGN_LEFT; int line = 0; int spaces = 0; - int height = 0; int wofs = 0; int spaces_size = 0; int align_ofs = 0; @@ -71,6 +107,13 @@ class BbCodeParser : Control { void _check_height(int m_height); //rename? bool _y_range_visible(int m_top, int m_height); +private: + //abstraction functions + bool _parse_text(ItemText *text); + bool _parse_table(ItemTable *table); + bool _parse_image(ItemImage *img); + bool _parse_detect_click(Item *previous_item); + private: //Copied functions Item *_get_next_item(Item *p_item, bool p_free = false) const; @@ -129,3 +172,5 @@ class BbCodeParser : Control { BbCodeParser(); ~BbCodeParser(); }; + +#endif // BBCODE_H From 1f6e1b1badda135b6877670a14cffd43f65429cc Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Fri, 5 Jun 2020 17:43:51 +0200 Subject: [PATCH 5/9] Split process_line in draw, cache and pointer methods ... Add _common_initalize_process. --- scene/gui/bbcode.cpp | 257 +++++++++++++++++++++++++++++++++++++++++-- scene/gui/bbcode.h | 3 +- 2 files changed, 248 insertions(+), 12 deletions(-) diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp index 0efc4140e8ac..ba44323e221d 100644 --- a/scene/gui/bbcode.cpp +++ b/scene/gui/bbcode.cpp @@ -84,9 +84,8 @@ bool BbCodeParser::_ensure_width(int m_width) { } if (wofs - backtrack + m_width > p_width) { line_wrapped = true; - if (p_mode == ProcessMode::PROCESS_CACHE) { - if (spaces > 0) - spaces -= 1; + if (p_mode == ProcessMode::PROCESS_CACHE && spaces > 0) { + spaces -= 1; } const bool x_in_range = (p_click_pos.x > p_ofs.x + wofs) && (!p_frame->cell || p_click_pos.x < p_ofs.x + p_width); if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh && x_in_range) { @@ -398,7 +397,7 @@ bool BbCodeParser::_parse_text(ItemText *text) { } if (_advance(fw)) { - return nonblank_line_count; + return true; } _check_height(fh); //must be done somewhere c = &c[end]; @@ -577,7 +576,7 @@ bool BbCodeParser::_parse_table(ItemTable *table) { total_height = row_height + vseparation; } if (_advance(table->total_width)) { - return nonblank_line_count; + return true; } _check_height(total_height); return false; @@ -615,7 +614,7 @@ bool BbCodeParser::_parse_image(ItemImage *img) { p_char_count++; if (_advance(img->size.width)) { - return nonblank_line_count; + return true; } _check_height(img->size.height + font->get_descent()); @@ -636,6 +635,40 @@ bool BbCodeParser::_parse_detect_click(Item *previous_item) { return false; } +void BbCodeParser::_common_initalize_process() { + l = p_frame->lines.write[p_line]; + it = l.from; + + line_ofs = 0; + margin = _find_margin(it, p_base_font); + align = _find_align(it); + line = 0; + spaces = 0; + + wofs = margin; + spaces_size = 0; + align_ofs = 0; + + cfont = _find_font(it); + if (cfont.is_null()) { + cfont = p_base_font; + } + + //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed + line_height = cfont->get_height(); + line_ascent = cfont->get_ascent(); + line_descent = cfont->get_descent(); + + backtrack = 0; // for dynamic hidden content. + nonblank_line_count = 0; //number of nonblank lines as counted during ProcessMode::PROCESS_DRAW + + rchar = 0; + lh = 0; + line_is_blank = true; + line_wrapped = false; + fh = 0; +} + BbCodeParser::Item *BbCodeParser::_get_next_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { @@ -968,9 +1001,9 @@ void BbCodeParser::start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, con wofs = margin; - /*if (p_mode != ProcessMode::PROCESS_CACHE && align != Align::ALIGN_FILL) { + if (p_mode != ProcessMode::PROCESS_CACHE && align != Align::ALIGN_FILL) { wofs += line_ofs; - }*/ + } begin = wofs; @@ -986,8 +1019,10 @@ void BbCodeParser::start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, con } void BbCodeParser::process_cache() { - ERR_FAIL_INDEX(line, l.offset_caches.size()); - line_ofs = l.offset_caches[line]; + ERR_FAIL_INDEX((int)p_mode, 3); + p_mode = ProcessMode::PROCESS_CACHE; + + _common_initalize_process(); l.offset_caches.clear(); l.height_caches.clear(); @@ -996,7 +1031,207 @@ void BbCodeParser::process_cache() { l.char_count = 0; l.minimum_width = 0; l.maximum_width = 0; + + begin = wofs; + + while (it) { + switch (it->type) { + case ItemType::ITEM_ALIGN: { + ItemAlign *align_it = static_cast(it); + align = align_it->align; + } break; + case ItemType::ITEM_INDENT: { + if (it == l.from) { + break; + } + ItemIndent *indent_it = static_cast(it); + int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; + margin += indent; + begin += indent; + wofs += indent; + } break; + case ItemType::ITEM_TEXT: { + if (_parse_text(static_cast(it))) { + return; + } + } break; + case ItemType::ITEM_IMAGE: { + if (_parse_image(static_cast(it))) { + return; + } + } break; + case ItemType::ITEM_TABLE: { + if (_parse_table(static_cast(it))) { + return; + } + + } break; + + default: { + } + } + + it = _get_next_item(it); + if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { + break; + } + } + + if (_new_line()) { + //Safe is safe. If somebody does something below _new_line we have a bug + return; + } +} + +int BbCodeParser::process_draw() { + ERR_FAIL_INDEX_V((int)p_mode, 3, 0); + p_mode = ProcessMode::PROCESS_DRAW; + + ci = get_canvas_item(); + + _common_initalize_process(); + + ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0); + line_ofs = l.offset_caches[line]; + + if (align != Align::ALIGN_FILL) { + wofs += line_ofs; + } + + begin = wofs; + selection_fg = get_theme_color("font_color_selected"); + selection_bg = get_theme_color("selection_color"); + + while (it) { + switch (it->type) { + case ItemType::ITEM_ALIGN: { + ItemAlign *align_it = static_cast(it); + align = align_it->align; + } break; + case ItemType::ITEM_INDENT: { + if (it == l.from) { + break; + } + ItemIndent *indent_it = static_cast(it); + int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; + margin += indent; + begin += indent; + wofs += indent; + } break; + case ItemType::ITEM_TEXT: { + if (_parse_text(static_cast(it))) { + return nonblank_line_count; + } + } break; + case ItemType::ITEM_IMAGE: { + if (_parse_image(static_cast(it))) { + return nonblank_line_count; + } + } break; + case ItemType::ITEM_NEWLINE: { + lh = 0; + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_is_blank = true; + + } break; + case ItemType::ITEM_TABLE: { + if (_parse_table(static_cast(it))) { + return nonblank_line_count; + } + + } break; + + default: { + } + } + + it = _get_next_item(it); + if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { + break; + } + } + if (_new_line()) { + //Safe is safe. If somebody does something below _new_line we have a bug + return nonblank_line_count; + } + + return nonblank_line_count; } -void BbCodeParser::process_draw() { +void BbCodeParser::process_pointer() { + ERR_FAIL_INDEX((int)p_mode, 3); + p_mode = ProcessMode::PROCESS_POINTER; + + if (r_outside) { + *r_outside = false; + } + + _common_initalize_process(); + + ERR_FAIL_INDEX(line, l.offset_caches.size()); + line_ofs = l.offset_caches[line]; + + if (align != Align::ALIGN_FILL) { + wofs += line_ofs; + } + + begin = wofs; + + while (it) { + switch (it->type) { + case ItemType::ITEM_ALIGN: { + ItemAlign *align_it = static_cast(it); + align = align_it->align; + } break; + case ItemType::ITEM_INDENT: { + if (it == l.from) { + break; + } + ItemIndent *indent_it = static_cast(it); + int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; + margin += indent; + begin += indent; + wofs += indent; + } break; + case ItemType::ITEM_TEXT: { + if (_parse_text(static_cast(it))) { + return; + } + } break; + case ItemType::ITEM_IMAGE: { + if (_parse_image(static_cast(it))) { + return; + } + } break; + case ItemType::ITEM_NEWLINE: { + lh = 0; + lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_is_blank = true; + } break; + case ItemType::ITEM_TABLE: { + if (_parse_table(static_cast(it))) { + return; + } + + } break; + + default: { + } + } + + Item *itp = it; + it = _get_next_item(it); + + if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { + if (_parse_detect_click(itp)) { + return; + } + break; + } + } + + if (_new_line()) { + //Safe is safe. If somebody does something below _new_line we have a bug + return; + } } diff --git a/scene/gui/bbcode.h b/scene/gui/bbcode.h index 700bd6198152..cc8b36dbf62f 100644 --- a/scene/gui/bbcode.h +++ b/scene/gui/bbcode.h @@ -113,6 +113,7 @@ class BbCodeParser : Label { bool _parse_table(ItemTable *table); bool _parse_image(ItemImage *img); bool _parse_detect_click(Item *previous_item); + void _common_initalize_process(); private: //Copied functions @@ -136,7 +137,7 @@ class BbCodeParser : Label { void start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, const int _p_height, const int _p_width, const int _p_line, const Ref &p_base_font); void process(); void process_cache(); - void process_draw(); + int process_draw(); void process_pointer(); int get_data(); From 8b091980c2c5ac70c95aa5312d6e64bc8e2fae99 Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Sat, 6 Jun 2020 15:01:45 +0200 Subject: [PATCH 6/9] Integrate BbCodeParser into RTL Should compile again. Some bugs are existent and some more refractoring needs to be done. --- scene/gui/bbcode.cpp | 179 +++++++++++++++++----------------- scene/gui/bbcode.h | 43 ++++---- scene/gui/rich_text_label.cpp | 22 +++-- scene/gui/rich_text_label.h | 4 +- 4 files changed, 130 insertions(+), 118 deletions(-) diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp index ba44323e221d..26db33513538 100644 --- a/scene/gui/bbcode.cpp +++ b/scene/gui/bbcode.cpp @@ -10,8 +10,58 @@ #include "editor/editor_scale.h" #endif -BbCodeParser::BbCodeParser() { - _process_line(p_frame, p_ofs, p_height, p_width, p_line, ProcessMode::PROCESS_CACHE, p_base_font, p_base_color, p_base_color, p_shadow_as_outline, shadow_ofs); +BbCodeParser::BbCodeParser(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_height, int _p_width, int _p_line, + const Ref &_p_base_font, const Color &_p_base_color, const Color &_p_font_color_shadow, + bool _p_shadow_as_outline, const Point2 &_shadow_ofs, RichTextLabel &_p_ci) : + l{ _p_frame->lines.write[_p_line] }, + ci{ _p_ci.get_canvas_item() }, + p_frame{ _p_frame }, + p_ofs{ _p_ofs }, + p_base_font{ _p_base_font }, + p_base_color{ _p_base_color }, + p_font_color_shadow{ _p_font_color_shadow }, + shadow_ofs{ _shadow_ofs }, + p_ci{ _p_ci }, + p_height{ _p_height }, + p_width{ _p_width }, + p_line{ _p_line }, + p_shadow_as_outline{ _p_shadow_as_outline } { + it = l.from; + line_ofs = 0; + margin = _find_margin(it, p_base_font); + align = _find_align(it); + line = 0; + spaces = 0; + + wofs = margin; + spaces_size = 0; + align_ofs = 0; + + cfont = _find_font(it); + if (cfont.is_null()) { + cfont = p_base_font; + } + + //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed + line_height = cfont->get_height(); + line_ascent = cfont->get_ascent(); + line_descent = cfont->get_descent(); + + backtrack = 0; // for dynamic hidden content. + nonblank_line_count = 0; //number of nonblank lines as counted during ProcessMode::PROCESS_DRAW + + rchar = 0; + lh = 0; + line_is_blank = true; + line_wrapped = false; + fh = 0; + + tab_size = p_ci.tab_size; + default_align = p_ci.default_align; + underline_meta = p_ci.underline_meta; + override_selected_font_color = p_ci.override_selected_font_color; + selection = p_ci.selection; + visible_characters = p_ci.visible_characters; } BbCodeParser::~BbCodeParser() {} @@ -54,7 +104,7 @@ bool BbCodeParser::_new_line() { l.space_caches.push_back(spaces); } line_wrapped = false; - p_height += line_height + get_theme_constant(SceneStringNames::get_singleton()->line_separation); + p_height += line_height + p_ci.get_theme_constant(SceneStringNames::get_singleton()->line_separation); line_height = 0; line_ascent = 0; line_descent = 0; @@ -119,7 +169,7 @@ void BbCodeParser::_check_height(int m_height) { } bool BbCodeParser::_y_range_visible(int m_top, int m_height) { - return (m_height > 0 && ((m_top >= 0 && m_top < get_size().y) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < get_size().y))); + return (m_height > 0 && ((m_top >= 0 && m_top < p_ci.get_size().y) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < p_ci.get_size().y))); } bool BbCodeParser::_parse_text(ItemText *text) { @@ -341,7 +391,7 @@ bool BbCodeParser::_parse_text(ItemText *text) { if (visible) { if (selected) { cw = font->get_char_size(fx_char, c[i + 1]).x; - draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + p_height, cw, lh), selection_bg); + p_ci.draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + p_height, cw, lh), selection_bg); } if (p_font_color_shadow.a > 0) { @@ -408,13 +458,13 @@ bool BbCodeParser::_parse_text(ItemText *text) { bool BbCodeParser::_parse_table(ItemTable *table) { lh = 0; - int hseparation = get_theme_constant("table_hseparation"); - int vseparation = get_theme_constant("table_vseparation"); + int hseparation = p_ci.get_theme_constant("table_hseparation"); + int vseparation = p_ci.get_theme_constant("table_vseparation"); Color ccolor = _find_color(table, p_base_color); Vector2 draw_ofs = Point2(wofs, p_height); - Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); - Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); + Color font_color_shadow = p_ci.get_theme_color("font_color_shadow"); + bool use_outline = p_ci.get_theme_constant("shadow_as_outline"); + Point2 shadow_ofs2(p_ci.get_theme_constant("shadow_offset_x"), p_ci.get_theme_constant("shadow_offset_y")); if (p_mode == ProcessMode::PROCESS_CACHE) { int idx = 0; @@ -436,7 +486,8 @@ bool BbCodeParser::_parse_table(ItemTable *table) { int ly = 0; for (int i = 0; i < frame->lines.size(); i++) { - _process_line(frame, Point2(), ly, available_width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + BbCodeParser(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, p_ci).process_cache(); + //_process_line(frame, Point2(), ly, available_width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); } @@ -512,7 +563,8 @@ bool BbCodeParser::_parse_table(ItemTable *table) { for (int i = 0; i < frame->lines.size(); i++) { int ly = 0; - _process_line(frame, Point2(), ly, table->columns[column].width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + BbCodeParser(frame, Point2(), ly, table->columns[column].width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, p_ci).process_cache(); + //_process_line(frame, Point2(), ly, table->columns[column].width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); frame->lines.write[i].height_cache = ly; //actual height frame->lines.write[i].height_accum_cache = ly; //actual height } @@ -537,7 +589,7 @@ bool BbCodeParser::_parse_table(ItemTable *table) { int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache); int lines_ofs = p_ofs.y + offset.y + draw_ofs.y; - bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0; + bool visible = lines_ofs < p_ci.get_size().height && lines_ofs + lines_h >= 0; if (visible) { line_is_blank = false; } @@ -545,9 +597,11 @@ bool BbCodeParser::_parse_table(ItemTable *table) { for (int i = 0; i < frame->lines.size(); i++) { if (visible) { if (p_mode == ProcessMode::PROCESS_DRAW) { - nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); + nonblank_line_count += BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_ci).process_draw(); + //nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); } else if (p_mode == ProcessMode::PROCESS_POINTER) { - _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); + BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_ci).process_pointer(p_click_pos, r_click_item, r_click_char, r_outside); + //_process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) { return nonblank_line_count; // exit early } @@ -636,37 +690,6 @@ bool BbCodeParser::_parse_detect_click(Item *previous_item) { } void BbCodeParser::_common_initalize_process() { - l = p_frame->lines.write[p_line]; - it = l.from; - - line_ofs = 0; - margin = _find_margin(it, p_base_font); - align = _find_align(it); - line = 0; - spaces = 0; - - wofs = margin; - spaces_size = 0; - align_ofs = 0; - - cfont = _find_font(it); - if (cfont.is_null()) { - cfont = p_base_font; - } - - //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed - line_height = cfont->get_height(); - line_ascent = cfont->get_ascent(); - line_descent = cfont->get_descent(); - - backtrack = 0; // for dynamic hidden content. - nonblank_line_count = 0; //number of nonblank lines as counted during ProcessMode::PROCESS_DRAW - - rchar = 0; - lh = 0; - line_is_blank = true; - line_wrapped = false; - fh = 0; } BbCodeParser::Item *BbCodeParser::_get_next_item(Item *p_item, bool p_free) const { @@ -848,6 +871,7 @@ void BbCodeParser::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) } } +/* int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { ERR_FAIL_INDEX_V((int)p_mode, 3, 0); @@ -985,41 +1009,9 @@ int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y return nonblank_line_count; } +*/ -void BbCodeParser::start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, const int p_line_height, const int _p_width, const int _p_line, const Ref &p_base_font) { - p_frame = _p_frame; - p_ofs = _p_ofs; - p_height = p_line_height; - p_width = _p_width; - p_line = _p_line; - - l = p_frame->lines.write[p_line]; - it = l.from; - - margin = _find_margin(it, p_base_font); - align = _find_align(it); - - wofs = margin; - - if (p_mode != ProcessMode::PROCESS_CACHE && align != Align::ALIGN_FILL) { - wofs += line_ofs; - } - - begin = wofs; - - cfont = _find_font(it); - if (cfont.is_null()) { - cfont = p_base_font; - } - - //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed - line_height = cfont->get_height(); - line_ascent = cfont->get_ascent(); - line_descent = cfont->get_descent(); -} - -void BbCodeParser::process_cache() { - ERR_FAIL_INDEX((int)p_mode, 3); +int BbCodeParser::process_cache() { p_mode = ProcessMode::PROCESS_CACHE; _common_initalize_process(); @@ -1052,17 +1044,17 @@ void BbCodeParser::process_cache() { } break; case ItemType::ITEM_TEXT: { if (_parse_text(static_cast(it))) { - return; + return p_height; } } break; case ItemType::ITEM_IMAGE: { if (_parse_image(static_cast(it))) { - return; + return p_height; } } break; case ItemType::ITEM_TABLE: { if (_parse_table(static_cast(it))) { - return; + return p_height; } } break; @@ -1079,15 +1071,14 @@ void BbCodeParser::process_cache() { if (_new_line()) { //Safe is safe. If somebody does something below _new_line we have a bug - return; + return p_height; } + return p_height; } -int BbCodeParser::process_draw() { - ERR_FAIL_INDEX_V((int)p_mode, 3, 0); +int BbCodeParser::process_draw(int _p_char_count) { p_mode = ProcessMode::PROCESS_DRAW; - - ci = get_canvas_item(); + p_char_count = _p_char_count; _common_initalize_process(); @@ -1099,8 +1090,8 @@ int BbCodeParser::process_draw() { } begin = wofs; - selection_fg = get_theme_color("font_color_selected"); - selection_bg = get_theme_color("selection_color"); + selection_fg = p_ci.get_theme_color("font_color_selected"); + selection_bg = p_ci.get_theme_color("selection_color"); while (it) { switch (it->type) { @@ -1158,10 +1149,14 @@ int BbCodeParser::process_draw() { return nonblank_line_count; } -void BbCodeParser::process_pointer() { - ERR_FAIL_INDEX((int)p_mode, 3); +void BbCodeParser::process_pointer(const Point2i &_p_click_pos, Item **_r_click_item, int *_r_click_char, bool *_r_outside) { p_mode = ProcessMode::PROCESS_POINTER; + p_click_pos = _p_click_pos; + r_click_item = _r_click_item; + r_click_char = _r_click_char; + r_outside = _r_outside; + if (r_outside) { *r_outside = false; } diff --git a/scene/gui/bbcode.h b/scene/gui/bbcode.h index cc8b36dbf62f..c778f14ecf26 100644 --- a/scene/gui/bbcode.h +++ b/scene/gui/bbcode.h @@ -35,8 +35,9 @@ #include "rich_text_effect.h" #include "scene/gui/rich_text_label.h" -class BbCodeParser : Label { - GDCLASS(BbCodeParser, Label); +class RichTextLabel; + +class BbCodeParser { //We use the classes from RTL here, to have a quick dependency overview //Later, we should move them over here, if we no longer have dependencies in the RTL using Line = RichTextLabel::Line; @@ -68,9 +69,9 @@ class BbCodeParser : Label { private: //localy declared vars in _process_line //Actual line processed by process line (determined using p_frame and p_line) - Line &l = Line(); + Line &l; Item *it = nullptr; - RID ci = get_canvas_item(); + RID ci; int line_ofs = 0; int margin = 0; Align align = Align::ALIGN_LEFT; @@ -127,7 +128,7 @@ class BbCodeParser : Label { bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); - int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); + //int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); //TODOS: // split process_modes into different functions @@ -136,9 +137,13 @@ class BbCodeParser : Label { public: void start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, const int _p_height, const int _p_width, const int _p_line, const Ref &p_base_font); void process(); - void process_cache(); - int process_draw(); - void process_pointer(); + + //Returns height + int process_cache(); + //Returns nonblank_lines + int process_draw(int _p_char_count = 0); + //Has to return the clicked item, if it was clicked, clicked char + void process_pointer(const Point2i &_p_click_pos, Item **_r_click_item, int *_r_click_char, bool *_r_outside); int get_data(); @@ -152,25 +157,29 @@ class BbCodeParser : Label { int visible_characters = -1; public: + RichTextLabel::ProcessMode p_mode; + Point2i p_click_pos_default = Point2i(); + //LineParser arguments ItemFrame *p_frame = nullptr; - Vector2 &p_ofs = Vector2(); - int p_height = 0; //is altered ONLY when a new line is added //was y + const Vector2 &p_ofs; + const Ref &p_base_font; + const Color &p_base_color; + const Color &p_font_color_shadow; + const Point2 &shadow_ofs; + RichTextLabel &p_ci; + int &p_height; //is altered ONLY when a new line is added //was y int p_width = 0; int p_line = 0; - RichTextLabel::ProcessMode p_mode = ProcessMode::PROCESS_DRAW; - Ref &p_base_font = Ref(); - Color &p_base_color = Color(); - Color &p_font_color_shadow = Color(); bool p_shadow_as_outline = false; - Point2 &shadow_ofs = Point2(); - Point2i &p_click_pos = Point2i(); + + Point2i &p_click_pos = p_click_pos_default; RichTextLabel::Item **r_click_item = nullptr; int *r_click_char = nullptr; bool *r_outside = nullptr; int p_char_count = 0; - BbCodeParser(); + BbCodeParser(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_height, int p_width, int p_line, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, RichTextLabel &p_ci); ~BbCodeParser(); }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 2648967586d9..b37054cf3f0f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "rich_text_label.h" +#include "bbcode.h" #include "core/math/math_defs.h" #include "core/os/keyboard.h" @@ -699,7 +700,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int ly = 0; for (int i = 0; i < frame->lines.size(); i++) { - _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + BbCodeParser(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, *this).process_cache(); + //_process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); } @@ -775,7 +777,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < frame->lines.size(); i++) { int ly = 0; - _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); + BbCodeParser(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, *this).process_cache(); + //_process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); frame->lines.write[i].height_cache = ly; //actual height frame->lines.write[i].height_accum_cache = ly; //actual height } @@ -808,9 +811,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < frame->lines.size(); i++) { if (visible) { if (p_mode == PROCESS_DRAW) { - nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); + nonblank_line_count += BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, *this).process_draw(); + //nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); } else if (p_mode == PROCESS_POINTER) { - _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); + BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, *this).process_pointer(p_click_pos, r_click_item, r_click_char, r_outside); + //_process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) { RETURN; // exit early } @@ -1020,7 +1025,8 @@ void RichTextLabel::_notification(int p_what) { visible_line_count = 0; while (y < size.height && from_line < main->lines.size()) { - visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars); + visible_line_count += BbCodeParser(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, *this).process_draw(total_chars); + //visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars); total_chars += main->lines[from_line].char_count; from_line++; @@ -1065,7 +1071,8 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Color base_color = get_theme_color("default_color"); while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) { - _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside); + BbCodeParser(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, *this).process_pointer(p_click, r_click_item, r_click_char, r_outside); + //_process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) { return; } @@ -1546,7 +1553,8 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) { int y = 0; - _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); + BbCodeParser(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, base_font, Color(), font_color_shadow, use_outline, shadow_ofs, *this).process_cache(); + //_process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); p_frame->lines.write[i].height_cache = y; p_frame->lines.write[i].height_accum_cache = y; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index fe75ab05bacd..981a885f3b74 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -35,8 +35,8 @@ #include "scene/gui/label.h" #include "scene/gui/scroll_bar.h" -class RichTextLabel : public Label { - GDCLASS(RichTextLabel, Label); +class RichTextLabel : public Control { + GDCLASS(RichTextLabel, Control); public: enum Align { From 3a23f7f2753f1dd124e91f9a0bbbef3113d7631e Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Sat, 6 Jun 2020 15:43:50 +0200 Subject: [PATCH 7/9] Remove _process_line from RTL --- scene/gui/bbcode.cpp | 2 +- scene/gui/rich_text_label.cpp | 741 ---------------------------------- scene/gui/rich_text_label.h | 1 - 3 files changed, 1 insertion(+), 743 deletions(-) diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp index 26db33513538..40f08fae8e75 100644 --- a/scene/gui/bbcode.cpp +++ b/scene/gui/bbcode.cpp @@ -663,7 +663,7 @@ bool BbCodeParser::_parse_image(ItemImage *img) { } if (p_mode == ProcessMode::PROCESS_DRAW && visible) { - img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, p_height + lh - font->get_descent() - img->size.height), img->size)); + img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, p_height + lh - font->get_descent() - img->size.height), img->size), false, img->color); } p_char_count++; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index b37054cf3f0f..c790bc052608 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -141,747 +141,6 @@ Rect2 RichTextLabel::_get_text_rect() { return Rect2(style->get_offset(), get_size() - style->get_minimum_size()); } -int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { - ERR_FAIL_INDEX_V((int)p_mode, 3, 0); - - RID ci; - if (r_outside) { - *r_outside = false; - } - if (p_mode == PROCESS_DRAW) { - ci = get_canvas_item(); - - if (r_click_item) { - *r_click_item = nullptr; - } - } - Line &l = p_frame->lines.write[p_line]; - Item *it = l.from; - - int line_ofs = 0; - int margin = _find_margin(it, p_base_font); - Align align = _find_align(it); - int line = 0; - int spaces = 0; - - int height = get_size().y; - - if (p_mode != PROCESS_CACHE) { - ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0); - line_ofs = l.offset_caches[line]; - } - - if (p_mode == PROCESS_CACHE) { - l.offset_caches.clear(); - l.height_caches.clear(); - l.ascent_caches.clear(); - l.descent_caches.clear(); - l.char_count = 0; - l.minimum_width = 0; - l.maximum_width = 0; - } - - int wofs = margin; - int spaces_size = 0; - int align_ofs = 0; - - if (p_mode != PROCESS_CACHE && align != ALIGN_FILL) { - wofs += line_ofs; - } - - int begin = wofs; - - Ref cfont = _find_font(it); - if (cfont.is_null()) { - cfont = p_base_font; - } - - //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed - int line_height = cfont->get_height(); - int line_ascent = cfont->get_ascent(); - int line_descent = cfont->get_descent(); - - int backtrack = 0; // for dynamic hidden content. - - int nonblank_line_count = 0; //number of nonblank lines as counted during PROCESS_DRAW - - Variant meta; - -#define RETURN return nonblank_line_count - -#define NEW_LINE \ - { \ - if (p_mode != PROCESS_CACHE) { \ - line++; \ - backtrack = 0; \ - if (!line_is_blank) { \ - nonblank_line_count++; \ - } \ - line_is_blank = true; \ - if (line < l.offset_caches.size()) \ - line_ofs = l.offset_caches[line]; \ - wofs = margin; \ - if (align != ALIGN_FILL) \ - wofs += line_ofs; \ - } else { \ - int used = wofs - margin; \ - switch (align) { \ - case ALIGN_LEFT: \ - l.offset_caches.push_back(0); \ - break; \ - case ALIGN_CENTER: \ - l.offset_caches.push_back(((p_width - margin) - used) / 2); \ - break; \ - case ALIGN_RIGHT: \ - l.offset_caches.push_back(((p_width - margin) - used)); \ - break; \ - case ALIGN_FILL: \ - l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); \ - break; \ - } \ - l.height_caches.push_back(line_height); \ - l.ascent_caches.push_back(line_ascent); \ - l.descent_caches.push_back(line_descent); \ - l.space_caches.push_back(spaces); \ - } \ - line_wrapped = false; \ - y += line_height + get_theme_constant(SceneStringNames::get_singleton()->line_separation); \ - line_height = 0; \ - line_ascent = 0; \ - line_descent = 0; \ - spaces = 0; \ - spaces_size = 0; \ - wofs = begin; \ - align_ofs = 0; \ - if (p_mode != PROCESS_CACHE) { \ - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \ - line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; \ - line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; \ - if (p_mode == PROCESS_DRAW) { \ - if (line < l.offset_caches.size()) { \ - wofs = l.offset_caches[line]; \ - } \ - } \ - } \ - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \ - if (r_outside) \ - *r_outside = true; \ - *r_click_item = it; \ - *r_click_char = rchar; \ - RETURN; \ - } \ - } - -#define ENSURE_WIDTH(m_width) \ - if (p_mode == PROCESS_CACHE) { \ - l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); \ - l.minimum_width = MAX(l.minimum_width, m_width); \ - } \ - if (wofs - backtrack + m_width > p_width) { \ - line_wrapped = true; \ - if (p_mode == PROCESS_CACHE) { \ - if (spaces > 0) \ - spaces -= 1; \ - } \ - const bool x_in_range = (p_click_pos.x > p_ofs.x + wofs) && (!p_frame->cell || p_click_pos.x < p_ofs.x + p_width); \ - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && x_in_range) { \ - if (r_outside) \ - *r_outside = true; \ - *r_click_item = it; \ - *r_click_char = rchar; \ - RETURN; \ - } \ - NEW_LINE \ - } - -#define ADVANCE(m_width) \ - { \ - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x >= p_ofs.x + wofs && p_click_pos.x < p_ofs.x + wofs + m_width) { \ - if (r_outside) \ - *r_outside = false; \ - *r_click_item = it; \ - *r_click_char = rchar; \ - RETURN; \ - } \ - wofs += m_width; \ - } - -#define CHECK_HEIGHT(m_height) \ - if (m_height > line_height) { \ - line_height = m_height; \ - } - -#define YRANGE_VISIBLE(m_top, m_height) \ - (m_height > 0 && ((m_top >= 0 && m_top < height) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < height))) - - Color selection_fg; - Color selection_bg; - - if (p_mode == PROCESS_DRAW) { - selection_fg = get_theme_color("font_color_selected"); - selection_bg = get_theme_color("selection_color"); - } - - int rchar = 0; - int lh = 0; - bool line_is_blank = true; - bool line_wrapped = false; - int fh = 0; - - while (it) { - switch (it->type) { - case ITEM_ALIGN: { - ItemAlign *align_it = static_cast(it); - - align = align_it->align; - - } break; - case ITEM_INDENT: { - if (it != l.from) { - ItemIndent *indent_it = static_cast(it); - - int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; - margin += indent; - begin += indent; - wofs += indent; - } - - } break; - case ITEM_TEXT: { - ItemText *text = static_cast(it); - - Ref font = _find_font(it); - if (font.is_null()) { - font = p_base_font; - } - - const CharType *c = text->text.c_str(); - const CharType *cf = c; - int ascent = font->get_ascent(); - int descent = font->get_descent(); - - Color color; - Color font_color_shadow; - bool underline = false; - bool strikethrough = false; - ItemFade *fade = nullptr; - int it_char_start = p_char_count; - - Vector fx_stack = Vector(); - _fetch_item_fx_stack(text, fx_stack); - bool custom_fx_ok = true; - - if (p_mode == PROCESS_DRAW) { - color = _find_color(text, p_base_color); - font_color_shadow = _find_color(text, p_font_color_shadow); - if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { - underline = true; - } else if (_find_strikethrough(text)) { - strikethrough = true; - } - - Item *fade_item = it; - while (fade_item) { - if (fade_item->type == ITEM_FADE) { - fade = static_cast(fade_item); - break; - } - fade_item = fade_item->parent; - } - - } else if (p_mode == PROCESS_CACHE) { - l.char_count += text->text.length(); - } - - rchar = 0; - FontDrawer drawer(font, Color(1, 1, 1)); - while (*c) { - int end = 0; - int w = 0; - int fw = 0; - - lh = 0; - - if (p_mode != PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; - line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; - } - while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) { - int cw = font->get_char_size(c[end], c[end + 1]).width; - if (c[end] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - } - - if (end > 0 && fw + cw + begin > p_width) { - break; //don't allow lines longer than assigned width - } - - fw += cw; - - end++; - } - CHECK_HEIGHT(fh); - ENSURE_WIDTH(fw); - - line_ascent = MAX(line_ascent, ascent); - line_descent = MAX(line_descent, descent); - fh = line_ascent + line_descent; - - if (end && c[end - 1] == ' ') { - if (p_mode == PROCESS_CACHE) { - spaces_size += font->get_char_size(' ').width; - } else if (align == ALIGN_FILL) { - int ln = MIN(l.offset_caches.size() - 1, line); - if (l.space_caches[ln]) { - align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln]; - } - } - spaces++; - } - - { - int ofs = 0 - backtrack; - - for (int i = 0; i < end; i++) { - int pofs = wofs + ofs; - - if (p_mode == PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { - int cw = font->get_char_size(c[i], c[i + 1]).x; - - if (c[i] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - } - - if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) { - rchar = int((&c[i]) - cf); - } - - ofs += cw; - } else if (p_mode == PROCESS_DRAW) { - bool selected = false; - Color fx_color = Color(color); - Point2 fx_offset; - CharType fx_char = c[i]; - - if (selection.active) { - int cofs = (&c[i]) - cf; - if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) { - selected = true; - } - } - - int cw = 0; - int c_item_offset = p_char_count - it_char_start; - - float faded_visibility = 1.0f; - if (fade) { - if (c_item_offset >= fade->starting_index) { - faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; - faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; - } - fx_color.a = faded_visibility; - } - - bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) && - faded_visibility > 0.0f); - - const bool previously_visible = visible; - - for (int j = 0; j < fx_stack.size(); j++) { - ItemFX *item_fx = fx_stack[j]; - - if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { - ItemCustomFX *item_custom = static_cast(item_fx); - - Ref charfx = item_custom->char_fx_transform; - Ref custom_effect = item_custom->custom_effect; - - if (!custom_effect.is_null()) { - charfx->elapsed_time = item_custom->elapsed_time; - charfx->relative_index = c_item_offset; - charfx->absolute_index = p_char_count; - charfx->visibility = visible; - charfx->offset = fx_offset; - charfx->color = fx_color; - charfx->character = fx_char; - - bool effect_status = custom_effect->_process_effect_impl(charfx); - custom_fx_ok = effect_status; - - fx_offset += charfx->offset; - fx_color = charfx->color; - visible &= charfx->visibility; - fx_char = charfx->character; - } - } else if (item_fx->type == ITEM_SHAKE) { - ItemShake *item_shake = static_cast(item_fx); - - uint64_t char_current_rand = item_shake->offset_random(c_item_offset); - uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); - uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); - n_time = (n_time > 1.0) ? 1.0 : n_time; - fx_offset += Point2(Math::lerp(Math::sin(previous_offset), - Math::sin(current_offset), - n_time), - Math::lerp(Math::cos(previous_offset), - Math::cos(current_offset), - n_time)) * - (float)item_shake->strength / 10.0f; - } else if (item_fx->type == ITEM_WAVE) { - ItemWave *item_wave = static_cast(item_fx); - - double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); - fx_offset += Point2(0, 1) * value; - } else if (item_fx->type == ITEM_TORNADO) { - ItemTornado *item_tornado = static_cast(item_fx); - - double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); - double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); - fx_offset += Point2(torn_x, torn_y); - } else if (item_fx->type == ITEM_RAINBOW) { - ItemRainbow *item_rainbow = static_cast(item_fx); - - fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), - item_rainbow->saturation, - item_rainbow->value, - fx_color.a); - } - } - - if (visible) { - line_is_blank = false; - w += font->get_char_size(c[i], c[i + 1]).x; - } - - if (c[i] == '\t') { - visible = false; - } - - if (visible) { - if (selected) { - cw = font->get_char_size(fx_char, c[i + 1]).x; - draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); - } - - if (p_font_color_shadow.a > 0) { - float x_ofs_shadow = align_ofs + pofs; - float y_ofs_shadow = y + lh - line_descent; - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - - if (p_shadow_as_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - } - } - - if (selected) { - drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); - } else { - cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); - } - } else if (previously_visible && c[i] != '\t') { - backtrack += font->get_char_size(fx_char, c[i + 1]).x; - } - - p_char_count++; - if (c[i] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - backtrack = MAX(0, backtrack - cw); - } - - ofs += cw; - } - } - - if (underline) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - line_descent + font->get_underline_position(); - float underline_width = font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - underline_width *= EDSCALE; -#endif - RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); - } else if (strikethrough) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - (line_ascent + line_descent) / 2; - float strikethrough_width = font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - strikethrough_width *= EDSCALE; -#endif - RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width); - } - } - - ADVANCE(fw); - CHECK_HEIGHT(fh); //must be done somewhere - c = &c[end]; - } - - } break; - case ITEM_IMAGE: { - lh = 0; - if (p_mode != PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - } else { - l.char_count += 1; //images count as chars too - } - - ItemImage *img = static_cast(it); - - Ref font = _find_font(it); - if (font.is_null()) { - font = p_base_font; - } - - if (p_mode == PROCESS_POINTER && r_click_char) { - *r_click_char = 0; - } - - ENSURE_WIDTH(img->size.width); - - bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->size.height, img->size.height)); - if (visible) { - line_is_blank = false; - } - - if (p_mode == PROCESS_DRAW && visible) { - img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size), false, img->color); - } - p_char_count++; - - ADVANCE(img->size.width); - CHECK_HEIGHT((img->size.height + font->get_descent())); - - } break; - case ITEM_NEWLINE: { - lh = 0; - - if (p_mode != PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - line_is_blank = true; - } - - } break; - case ITEM_TABLE: { - lh = 0; - ItemTable *table = static_cast(it); - int hseparation = get_theme_constant("table_hseparation"); - int vseparation = get_theme_constant("table_vseparation"); - Color ccolor = _find_color(table, p_base_color); - Vector2 draw_ofs = Point2(wofs, y); - Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); - Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); - - if (p_mode == PROCESS_CACHE) { - int idx = 0; - //set minimums to zero - for (int i = 0; i < table->columns.size(); i++) { - table->columns.write[i].min_width = 0; - table->columns.write[i].max_width = 0; - table->columns.write[i].width = 0; - } - //compute minimum width for each cell - const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; - - for (List::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(E->get()); - - int column = idx % table->columns.size(); - - int ly = 0; - - for (int i = 0; i < frame->lines.size(); i++) { - BbCodeParser(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, *this).process_cache(); - //_process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); - table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); - table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); - } - idx++; - } - - //compute available width and total ratio (for expanders) - - int total_ratio = 0; - int remaining_width = available_width; - table->total_width = hseparation; - - for (int i = 0; i < table->columns.size(); i++) { - remaining_width -= table->columns[i].min_width; - if (table->columns[i].max_width > table->columns[i].min_width) { - table->columns.write[i].expand = true; - } - if (table->columns[i].expand) { - total_ratio += table->columns[i].expand_ratio; - } - } - - //assign actual widths - for (int i = 0; i < table->columns.size(); i++) { - table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { - table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; - } - table->total_width += table->columns[i].width + hseparation; - } - - //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 < table->columns.size(); 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.write[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 < table->columns.size(); 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.write[i].width += incr; - table->total_width += incr; - } - } - } - } - } - - //compute caches properly again with the right width - idx = 0; - for (List::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(E->get()); - - int column = idx % table->columns.size(); - - for (int i = 0; i < frame->lines.size(); i++) { - int ly = 0; - BbCodeParser(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, *this).process_cache(); - //_process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); - frame->lines.write[i].height_cache = ly; //actual height - frame->lines.write[i].height_accum_cache = ly; //actual height - } - idx++; - } - } - - Point2 offset(align_ofs + hseparation, vseparation); - - int row_height = 0; - //draw using computed caches - int idx = 0; - for (List::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(E->get()); - - int column = idx % table->columns.size(); - - int ly = 0; - int yofs = 0; - - int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache); - int lines_ofs = p_ofs.y + offset.y + draw_ofs.y; - - bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0; - if (visible) { - line_is_blank = false; - } - - for (int i = 0; i < frame->lines.size(); i++) { - if (visible) { - if (p_mode == PROCESS_DRAW) { - nonblank_line_count += BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, *this).process_draw(); - //nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); - } else if (p_mode == PROCESS_POINTER) { - BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, *this).process_pointer(p_click_pos, r_click_item, r_click_char, r_outside); - //_process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); - if (r_click_item && *r_click_item) { - RETURN; // exit early - } - } - } - - yofs += frame->lines[i].height_cache; - if (p_mode == PROCESS_CACHE) { - frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; - } - } - - row_height = MAX(yofs, row_height); - offset.x += table->columns[column].width + hseparation; - - if (column == table->columns.size() - 1) { - offset.y += row_height + vseparation; - offset.x = hseparation; - row_height = 0; - } - idx++; - } - - int total_height = offset.y; - if (row_height) { - total_height = row_height + vseparation; - } - - ADVANCE(table->total_width); - CHECK_HEIGHT(total_height); - - } break; - - default: { - } - } - - Item *itp = it; - - it = _get_next_item(it); - - if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { - //went to next line, but pointer was on the previous one - if (r_outside) { - *r_outside = true; - } - *r_click_item = itp; - *r_click_char = rchar; - RETURN; - } - - break; - } - } - NEW_LINE; - - RETURN; - -#undef RETURN -#undef NEW_LINE -#undef ENSURE_WIDTH -#undef ADVANCE -#undef CHECK_HEIGHT -} - void RichTextLabel::_scroll_changed(double) { if (updating_scroll) { return; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 981a885f3b74..be987f378736 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -375,7 +375,6 @@ class RichTextLabel : public Control { int visible_characters = -1; float percent_visible = 1; - int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); //used in _process_line, _find_margin, search From 889f0498fa97f40a73e4be4a6b4fb7b322cb34b8 Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Sat, 6 Jun 2020 17:32:55 +0200 Subject: [PATCH 8/9] Clean up of the files --- scene/gui/bbcode.cpp | 2 +- scene/gui/bbcode.h | 17 +++--- scene/gui/rich_text_label.cpp | 98 ----------------------------------- scene/gui/rich_text_label.h | 25 --------- 4 files changed, 9 insertions(+), 133 deletions(-) diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp index 40f08fae8e75..df005ec18bc1 100644 --- a/scene/gui/bbcode.cpp +++ b/scene/gui/bbcode.cpp @@ -654,7 +654,7 @@ bool BbCodeParser::_parse_image(ItemImage *img) { } if (_ensure_width(img->size.width)) { - return nonblank_line_count; + return true; } bool visible = visible_characters < 0 || (p_char_count < visible_characters && _y_range_visible(p_height + lh - font->get_descent() - img->size.height, img->size.height)); diff --git a/scene/gui/bbcode.h b/scene/gui/bbcode.h index c778f14ecf26..424e69c1d0c6 100644 --- a/scene/gui/bbcode.h +++ b/scene/gui/bbcode.h @@ -43,7 +43,6 @@ class BbCodeParser { using Line = RichTextLabel::Line; using Item = RichTextLabel::Item; using ItemFrame = RichTextLabel::ItemFrame; - using ProcessMode = RichTextLabel::ProcessMode; using Align = RichTextLabel::Align; using ItemType = RichTextLabel::ItemType; using ItemAlign = RichTextLabel::ItemAlign; @@ -66,6 +65,13 @@ class BbCodeParser { protected: static void _bind_methods(); +private: + enum ProcessMode { + PROCESS_CACHE, + PROCESS_DRAW, + PROCESS_POINTER + }; + private: //localy declared vars in _process_line //Actual line processed by process line (determined using p_frame and p_line) @@ -128,16 +134,11 @@ class BbCodeParser { bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); - //int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); - //TODOS: // split process_modes into different functions // get rid of the Process Modes public: - void start_process(ItemFrame *_p_frame, const Vector2 &_p_ofs, const int _p_height, const int _p_width, const int _p_line, const Ref &p_base_font); - void process(); - //Returns height int process_cache(); //Returns nonblank_lines @@ -145,8 +146,6 @@ class BbCodeParser { //Has to return the clicked item, if it was clicked, clicked char void process_pointer(const Point2i &_p_click_pos, Item **_r_click_item, int *_r_click_char, bool *_r_outside); - int get_data(); - public: //Duplicated vars int tab_size = 4; @@ -157,7 +156,7 @@ class BbCodeParser { int visible_characters = -1; public: - RichTextLabel::ProcessMode p_mode; + ProcessMode p_mode; Point2i p_click_pos_default = Point2i(); //LineParser arguments diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index c790bc052608..ea200da7a6bc 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -602,93 +602,6 @@ Ref RichTextLabel::_find_font(Item *p_item) { return Ref(); } -int RichTextLabel::_find_margin(Item *p_item, const Ref &p_base_font) { - Item *item = p_item; - - int margin = 0; - - while (item) { - if (item->type == ITEM_INDENT) { - Ref font = _find_font(item); - if (font.is_null()) { - font = p_base_font; - } - - ItemIndent *indent = static_cast(item); - - margin += indent->level * tab_size * font->get_char_size(' ').width; - - } else if (item->type == ITEM_LIST) { - Ref font = _find_font(item); - if (font.is_null()) { - font = p_base_font; - } - } - - item = item->parent; - } - - return margin; -} - -RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) { - Item *item = p_item; - - while (item) { - if (item->type == ITEM_ALIGN) { - ItemAlign *align = static_cast(item); - return align->align; - } - - item = item->parent; - } - - return default_align; -} - -Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) { - Item *item = p_item; - - while (item) { - if (item->type == ITEM_COLOR) { - ItemColor *color = static_cast(item); - return color->color; - } - - item = item->parent; - } - - return p_default_color; -} - -bool RichTextLabel::_find_underline(Item *p_item) { - Item *item = p_item; - - while (item) { - if (item->type == ITEM_UNDERLINE) { - return true; - } - - item = item->parent; - } - - return false; -} - -bool RichTextLabel::_find_strikethrough(Item *p_item) { - Item *item = p_item; - - while (item) { - if (item->type == ITEM_STRIKETHROUGH) { - return true; - } - - item = item->parent; - } - - return false; -} - bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) { ERR_FAIL_INDEX_V((int)p_type, 19, false); @@ -703,17 +616,6 @@ bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) { return false; } -void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) { - Item *item = p_item; - while (item) { - if (item->type == ITEM_CUSTOMFX || item->type == ITEM_SHAKE || item->type == ITEM_WAVE || item->type == ITEM_TORNADO || item->type == ITEM_RAINBOW) { - r_stack.push_back(static_cast(item)); - } - - item = item->parent; - } -} - Color RichTextLabel::_get_color_from_string(const String &p_color_str, const Color &p_default_color) { if (p_color_str.begins_with("#")) { return Color::html(p_color_str); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index be987f378736..84516c22390f 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -346,17 +346,6 @@ class RichTextLabel : public Control { void _add_item(Item *p_item, bool p_enter = false, bool p_ensure_newline = false); void _remove_item(Item *p_item, const int p_line, const int p_subitem_line); - struct ProcessState { - int line_width; - }; - - enum ProcessMode { - - PROCESS_CACHE, - PROCESS_DRAW, - PROCESS_POINTER - }; - struct Selection { Item *click; int click_char; @@ -380,22 +369,10 @@ class RichTextLabel : public Control { //used in _process_line, _find_margin, search //duplicated in bbcode, check removal Ref _find_font(Item *p_item); - // used once in _process_line - int _find_margin(Item *p_item, const Ref &p_base_font); - // used once in _process_line - Align _find_align(Item *p_item); - // only used in _procces_line - Color _find_color(Item *p_item, const Color &p_default_color); - //used once in _process_line - bool _find_underline(Item *p_item); - //used once in _process_line - bool _find_strikethrough(Item *p_item); //used multiple times bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); bool _find_layout_subitem(Item *from, Item *to); bool _find_by_type(Item *p_item, ItemType p_type); - //only used once in _process_line, duplicated into bbcode - void _fetch_item_fx_stack(Item *p_item, Vector &r_stack); static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color); @@ -414,8 +391,6 @@ class RichTextLabel : public Control { bool use_bbcode = false; String bbcode; - void _update_all_lines(); - int fixed_width = -1; //Temporary to make it compile asap From b825e9e4e7c6d2a8f57c64fc17b833a79724d23f Mon Sep 17 00:00:00 2001 From: HaSa1002 Date: Sat, 13 Jun 2020 19:24:25 +0200 Subject: [PATCH 9/9] Start Work on second approach --- scene/gui/bbcode.cpp | 60 +++++++++++++++++------------------ scene/gui/bbcode.h | 8 ++--- scene/gui/bbcode_parser.h | 47 +++++++++++++++++++++++++++ scene/gui/rich_text_label.cpp | 6 ++-- scene/gui/rich_text_label.h | 2 +- 5 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 scene/gui/bbcode_parser.h diff --git a/scene/gui/bbcode.cpp b/scene/gui/bbcode.cpp index df005ec18bc1..14ff4cb645d4 100644 --- a/scene/gui/bbcode.cpp +++ b/scene/gui/bbcode.cpp @@ -10,7 +10,7 @@ #include "editor/editor_scale.h" #endif -BbCodeParser::BbCodeParser(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_height, int _p_width, int _p_line, +BbCodeProcess::BbCodeProcess(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_height, int _p_width, int _p_line, const Ref &_p_base_font, const Color &_p_base_color, const Color &_p_font_color_shadow, bool _p_shadow_as_outline, const Point2 &_shadow_ofs, RichTextLabel &_p_ci) : l{ _p_frame->lines.write[_p_line] }, @@ -64,12 +64,12 @@ BbCodeParser::BbCodeParser(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_h visible_characters = p_ci.visible_characters; } -BbCodeParser::~BbCodeParser() {} +BbCodeProcess::~BbCodeProcess() {} -void BbCodeParser::_bind_methods() { +void BbCodeProcess::_bind_methods() { } -bool BbCodeParser::_new_line() { +bool BbCodeProcess::_new_line() { if (p_mode != ProcessMode::PROCESS_CACHE) { line++; backtrack = 0; @@ -127,7 +127,7 @@ bool BbCodeParser::_new_line() { return false; } -bool BbCodeParser::_ensure_width(int m_width) { +bool BbCodeProcess::_ensure_width(int m_width) { if (p_mode == ProcessMode::PROCESS_CACHE) { l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); l.minimum_width = MAX(l.minimum_width, m_width); @@ -150,7 +150,7 @@ bool BbCodeParser::_ensure_width(int m_width) { return false; } -bool BbCodeParser::_advance(int m_width) { +bool BbCodeProcess::_advance(int m_width) { if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh && p_click_pos.x >= p_ofs.x + wofs && p_click_pos.x < p_ofs.x + wofs + m_width) { if (r_outside) *r_outside = false; @@ -162,17 +162,17 @@ bool BbCodeParser::_advance(int m_width) { return false; } -void BbCodeParser::_check_height(int m_height) { +void BbCodeProcess::_check_height(int m_height) { if (m_height > line_height) { line_height = m_height; } } -bool BbCodeParser::_y_range_visible(int m_top, int m_height) { +bool BbCodeProcess::_y_range_visible(int m_top, int m_height) { return (m_height > 0 && ((m_top >= 0 && m_top < p_ci.get_size().y) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < p_ci.get_size().y))); } -bool BbCodeParser::_parse_text(ItemText *text) { +bool BbCodeProcess::_parse_text(ItemText *text) { Ref font = _find_font(it); if (font.is_null()) { font = p_base_font; @@ -456,7 +456,7 @@ bool BbCodeParser::_parse_text(ItemText *text) { return false; } -bool BbCodeParser::_parse_table(ItemTable *table) { +bool BbCodeProcess::_parse_table(ItemTable *table) { lh = 0; int hseparation = p_ci.get_theme_constant("table_hseparation"); int vseparation = p_ci.get_theme_constant("table_vseparation"); @@ -486,7 +486,7 @@ bool BbCodeParser::_parse_table(ItemTable *table) { int ly = 0; for (int i = 0; i < frame->lines.size(); i++) { - BbCodeParser(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, p_ci).process_cache(); + BbCodeProcess(frame, Point2(), ly, available_width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, p_ci).process_cache(); //_process_line(frame, Point2(), ly, available_width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); @@ -563,7 +563,7 @@ bool BbCodeParser::_parse_table(ItemTable *table) { for (int i = 0; i < frame->lines.size(); i++) { int ly = 0; - BbCodeParser(frame, Point2(), ly, table->columns[column].width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, p_ci).process_cache(); + BbCodeProcess(frame, Point2(), ly, table->columns[column].width, i, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2, p_ci).process_cache(); //_process_line(frame, Point2(), ly, table->columns[column].width, i, ProcessMode::PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); frame->lines.write[i].height_cache = ly; //actual height frame->lines.write[i].height_accum_cache = ly; //actual height @@ -597,10 +597,10 @@ bool BbCodeParser::_parse_table(ItemTable *table) { for (int i = 0; i < frame->lines.size(); i++) { if (visible) { if (p_mode == ProcessMode::PROCESS_DRAW) { - nonblank_line_count += BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_ci).process_draw(); + nonblank_line_count += BbCodeProcess(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_ci).process_draw(); //nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); } else if (p_mode == ProcessMode::PROCESS_POINTER) { - BbCodeParser(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_ci).process_pointer(p_click_pos, r_click_item, r_click_char, r_outside); + BbCodeProcess(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_ci).process_pointer(p_click_pos, r_click_item, r_click_char, r_outside); //_process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, ProcessMode::PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) { return nonblank_line_count; // exit early @@ -636,7 +636,7 @@ bool BbCodeParser::_parse_table(ItemTable *table) { return false; } -bool BbCodeParser::_parse_image(ItemImage *img) { +bool BbCodeProcess::_parse_image(ItemImage *img) { lh = 0; if (p_mode != ProcessMode::PROCESS_CACHE) { lh = line < l.height_caches.size() ? l.height_caches[line] : 1; @@ -676,7 +676,7 @@ bool BbCodeParser::_parse_image(ItemImage *img) { return false; } -bool BbCodeParser::_parse_detect_click(Item *previous_item) { +bool BbCodeProcess::_parse_detect_click(Item *previous_item) { if (p_mode == ProcessMode::PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + p_height && p_click_pos.y <= p_ofs.y + p_height + lh) { //went to next line, but pointer was on the previous one if (r_outside) { @@ -689,10 +689,10 @@ bool BbCodeParser::_parse_detect_click(Item *previous_item) { return false; } -void BbCodeParser::_common_initalize_process() { +void BbCodeProcess::_common_initalize_process() { } -BbCodeParser::Item *BbCodeParser::_get_next_item(Item *p_item, bool p_free) const { +BbCodeProcess::Item *BbCodeProcess::_get_next_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { return p_item->subitems.front()->get(); @@ -737,7 +737,7 @@ BbCodeParser::Item *BbCodeParser::_get_next_item(Item *p_item, bool p_free) cons return nullptr; } -Ref BbCodeParser::_find_font(Item *p_item) { +Ref BbCodeProcess::_find_font(Item *p_item) { Item *fontitem = p_item; while (fontitem) { @@ -752,7 +752,7 @@ Ref BbCodeParser::_find_font(Item *p_item) { return Ref(); } -int BbCodeParser::_find_margin(Item *p_item, const Ref &p_base_font) { +int BbCodeProcess::_find_margin(Item *p_item, const Ref &p_base_font) { Item *item = p_item; int margin = 0; @@ -781,7 +781,7 @@ int BbCodeParser::_find_margin(Item *p_item, const Ref &p_base_font) { return margin; } -BbCodeParser::Align BbCodeParser::_find_align(Item *p_item) { +BbCodeProcess::Align BbCodeProcess::_find_align(Item *p_item) { Item *item = p_item; while (item) { @@ -796,7 +796,7 @@ BbCodeParser::Align BbCodeParser::_find_align(Item *p_item) { return default_align; } -Color BbCodeParser::_find_color(Item *p_item, const Color &p_default_color) { +Color BbCodeProcess::_find_color(Item *p_item, const Color &p_default_color) { Item *item = p_item; while (item) { @@ -811,7 +811,7 @@ Color BbCodeParser::_find_color(Item *p_item, const Color &p_default_color) { return p_default_color; } -bool BbCodeParser::_find_underline(Item *p_item) { +bool BbCodeProcess::_find_underline(Item *p_item) { Item *item = p_item; while (item) { @@ -825,7 +825,7 @@ bool BbCodeParser::_find_underline(Item *p_item) { return false; } -bool BbCodeParser::_find_strikethrough(Item *p_item) { +bool BbCodeProcess::_find_strikethrough(Item *p_item) { Item *item = p_item; while (item) { @@ -839,7 +839,7 @@ bool BbCodeParser::_find_strikethrough(Item *p_item) { return false; } -bool BbCodeParser::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { +bool BbCodeProcess::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { Item *item = p_item; while (item) { @@ -860,7 +860,7 @@ bool BbCodeParser::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) return false; } -void BbCodeParser::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) { +void BbCodeProcess::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) { Item *item = p_item; while (item) { if (item->type == ItemType::ITEM_CUSTOMFX || item->type == ItemType::ITEM_SHAKE || item->type == ItemType::ITEM_WAVE || item->type == ItemType::ITEM_TORNADO || item->type == ItemType::ITEM_RAINBOW) { @@ -872,7 +872,7 @@ void BbCodeParser::_fetch_item_fx_stack(Item *p_item, Vector &r_stack) } /* -int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { +int BbCodeProcess::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { ERR_FAIL_INDEX_V((int)p_mode, 3, 0); if (r_outside) { @@ -1011,7 +1011,7 @@ int BbCodeParser::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y } */ -int BbCodeParser::process_cache() { +int BbCodeProcess::process_cache() { p_mode = ProcessMode::PROCESS_CACHE; _common_initalize_process(); @@ -1076,7 +1076,7 @@ int BbCodeParser::process_cache() { return p_height; } -int BbCodeParser::process_draw(int _p_char_count) { +int BbCodeProcess::process_draw(int _p_char_count) { p_mode = ProcessMode::PROCESS_DRAW; p_char_count = _p_char_count; @@ -1149,7 +1149,7 @@ int BbCodeParser::process_draw(int _p_char_count) { return nonblank_line_count; } -void BbCodeParser::process_pointer(const Point2i &_p_click_pos, Item **_r_click_item, int *_r_click_char, bool *_r_outside) { +void BbCodeProcess::process_pointer(const Point2i &_p_click_pos, Item **_r_click_item, int *_r_click_char, bool *_r_outside) { p_mode = ProcessMode::PROCESS_POINTER; p_click_pos = _p_click_pos; diff --git a/scene/gui/bbcode.h b/scene/gui/bbcode.h index 424e69c1d0c6..ab8fd6c949a6 100644 --- a/scene/gui/bbcode.h +++ b/scene/gui/bbcode.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* panel.h */ +/* bbcode.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -37,7 +37,7 @@ class RichTextLabel; -class BbCodeParser { +class BbCodeProcess { //We use the classes from RTL here, to have a quick dependency overview //Later, we should move them over here, if we no longer have dependencies in the RTL using Line = RichTextLabel::Line; @@ -178,8 +178,8 @@ class BbCodeParser { bool *r_outside = nullptr; int p_char_count = 0; - BbCodeParser(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_height, int p_width, int p_line, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, RichTextLabel &p_ci); - ~BbCodeParser(); + BbCodeProcess(ItemFrame *_p_frame, const Vector2 &_p_ofs, int &_p_height, int p_width, int p_line, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, RichTextLabel &p_ci); + ~BbCodeProcess(); }; #endif // BBCODE_H diff --git a/scene/gui/bbcode_parser.h b/scene/gui/bbcode_parser.h new file mode 100644 index 000000000000..6c016b567421 --- /dev/null +++ b/scene/gui/bbcode_parser.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* panel.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef BBCODE_PARSER_H +#define BBCODE_PARSER_H + +#include "core/typedefs.h" +#include "core/ustring.h" + +class BbCodeParser { +public: + void append_text(const String &text); + void set_text(const String &text); + String get_text() const; + +private: + String text; +}; + +#endif diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index ea200da7a6bc..5dbfa39cc70e 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -284,7 +284,7 @@ void RichTextLabel::_notification(int p_what) { visible_line_count = 0; while (y < size.height && from_line < main->lines.size()) { - visible_line_count += BbCodeParser(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, *this).process_draw(total_chars); + visible_line_count += BbCodeProcess(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, *this).process_draw(total_chars); //visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars); total_chars += main->lines[from_line].char_count; @@ -330,7 +330,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Color base_color = get_theme_color("default_color"); while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) { - BbCodeParser(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, *this).process_pointer(p_click, r_click_item, r_click_char, r_outside); + BbCodeProcess(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, *this).process_pointer(p_click, r_click_item, r_click_char, r_outside); //_process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) { return; @@ -714,7 +714,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) { int y = 0; - BbCodeParser(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, base_font, Color(), font_color_shadow, use_outline, shadow_ofs, *this).process_cache(); + BbCodeProcess(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, base_font, Color(), font_color_shadow, use_outline, shadow_ofs, *this).process_cache(); //_process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); p_frame->lines.write[i].height_cache = y; p_frame->lines.write[i].height_accum_cache = y; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 84516c22390f..192652b8edb9 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -395,7 +395,7 @@ class RichTextLabel : public Control { //Temporary to make it compile asap //Check later, what belongs in which class and fix dependencies and move stuff around - friend class BbCodeParser; + friend class BbCodeProcess; protected: void _notification(int p_what);