diff options
author | Rémi Verschelde <rverschelde@gmail.com> | 2023-09-26 13:44:46 +0200 |
---|---|---|
committer | Rémi Verschelde <rverschelde@gmail.com> | 2023-09-26 13:44:46 +0200 |
commit | ca8b229e1c7dcf1123ae0d24f331860b789df86c (patch) | |
tree | 8d4a9e4de1b3d81fb9e2295304797c5e41123bcb /scene/gui | |
parent | 3ec673085b161c6d0b134df11d2a63874066553d (diff) | |
parent | bc6585a5a7380c39d182944e47592587f8b97231 (diff) | |
download | redot-engine-ca8b229e1c7dcf1123ae0d24f331860b789df86c.tar.gz |
Merge pull request #80410 from bruvzg/rtl_img_features
[RTL] Add support for image dynamic updating, padding, tooltips and size in percent.
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/rich_text_label.compat.inc | 41 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 224 | ||||
-rw-r--r-- | scene/gui/rich_text_label.h | 28 |
3 files changed, 250 insertions, 43 deletions
diff --git a/scene/gui/rich_text_label.compat.inc b/scene/gui/rich_text_label.compat.inc new file mode 100644 index 0000000000..a23c3b94f8 --- /dev/null +++ b/scene/gui/rich_text_label.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* rich_text_label.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DISABLE_DEPRECATED + +void RichTextLabel::_add_image_bind_compat_80410(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region) { + add_image(p_image, p_width, p_height, p_color, p_alignment, p_region, Variant(), false, String(), false); +} + +void RichTextLabel::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::_add_image_bind_compat_80410, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2())); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index cd821c7612..b4cd201e6a 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 "rich_text_label.compat.inc" #include "core/input/input_map.h" #include "core/math/math_defs.h" @@ -301,6 +302,14 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { switch (it->type) { + case ITEM_IMAGE: { + ItemImage *img = static_cast<ItemImage *>(it); + Size2 img_size = img->size; + if (img->size_in_percent) { + img_size = _get_image_size(img->image, p_width * img->rq_size.width / 100.f, p_width * img->rq_size.height / 100.f, img->region); + l.text_buf->resize_object((uint64_t)it, img_size, img->inline_align, 1); + } + } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); int col_count = table->columns.size(); @@ -575,7 +584,11 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } break; case ITEM_IMAGE: { ItemImage *img = static_cast<ItemImage *>(it); - l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1); + Size2 img_size = img->size; + if (img->size_in_percent) { + img_size = _get_image_size(img->image, p_width * img->rq_size.width / 100.f, p_width * img->rq_size.height / 100.f, img->region); + } + l.text_buf->add_object((uint64_t)it, img_size, img->inline_align, 1); txt += String::chr(0xfffc); l.char_count++; remaining_characters--; @@ -933,7 +946,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o switch (it->type) { case ITEM_IMAGE: { ItemImage *img = static_cast<ItemImage *>(it); - img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color); + if (img->pad) { + Size2 pad_size = Size2(MIN(rect.size.x, img->image->get_width()), MIN(rect.size.y, img->image->get_height())); + Vector2 pad_off = (rect.size - pad_size) / 2; + img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off + pad_off, pad_size), false, img->color); + } else { + img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color); + } } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); @@ -2215,11 +2234,15 @@ String RichTextLabel::get_tooltip(const Point2 &p_pos) const { const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &c_item, nullptr, &outside, true); String description; - if (c_item && !outside && const_cast<RichTextLabel *>(this)->_find_hint(c_item, &description)) { - return description; - } else { - return Control::get_tooltip(p_pos); + if (c_item && !outside) { + if (const_cast<RichTextLabel *>(this)->_find_hint(c_item, &description)) { + return description; + } else if (c_item->type == ITEM_IMAGE && !static_cast<ItemImage *>(c_item)->tooltip.is_empty()) { + return static_cast<ItemImage *>(c_item)->tooltip; + } } + + return Control::get_tooltip(p_pos); } void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) { @@ -3121,69 +3144,157 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub memdelete(p_item); } -void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region) { - _stop_thread(); - MutexLock data_lock(data_mutex); - - if (current->type == ITEM_TABLE) { - return; - } - - ERR_FAIL_COND(p_image.is_null()); - ERR_FAIL_COND(p_image->get_width() == 0); - ERR_FAIL_COND(p_image->get_height() == 0); - ItemImage *item = memnew(ItemImage); - - if (p_region.has_area()) { - Ref<AtlasTexture> atlas_tex = memnew(AtlasTexture); - atlas_tex->set_atlas(p_image); - atlas_tex->set_region(p_region); - item->image = atlas_tex; - } else { - item->image = p_image; - } - - item->color = p_color; - item->inline_align = p_alignment; - +Size2 RichTextLabel::_get_image_size(const Ref<Texture2D> &p_image, int p_width, int p_height, const Rect2 &p_region) { + Size2 ret; if (p_width > 0) { // custom width - item->size.width = p_width; + ret.width = p_width; if (p_height > 0) { // custom height - item->size.height = p_height; + ret.height = p_height; } else { // calculate height to keep aspect ratio if (p_region.has_area()) { - item->size.height = p_region.get_size().height * p_width / p_region.get_size().width; + ret.height = p_region.get_size().height * p_width / p_region.get_size().width; } else { - item->size.height = p_image->get_height() * p_width / p_image->get_width(); + ret.height = p_image->get_height() * p_width / p_image->get_width(); } } } else { if (p_height > 0) { // custom height - item->size.height = p_height; + ret.height = p_height; // calculate width to keep aspect ratio if (p_region.has_area()) { - item->size.width = p_region.get_size().width * p_height / p_region.get_size().height; + ret.width = p_region.get_size().width * p_height / p_region.get_size().height; } else { - item->size.width = p_image->get_width() * p_height / p_image->get_height(); + ret.width = p_image->get_width() * p_height / p_image->get_height(); } } else { if (p_region.has_area()) { // if the image has a region, keep the region size - item->size = p_region.get_size(); + ret = p_region.get_size(); } else { // keep original width and height - item->size = p_image->get_size(); + ret = p_image->get_size(); } } } + return ret; +} + +void RichTextLabel::add_image(const Ref<Texture2D> &p_image, int p_width, int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region, const Variant &p_key, bool p_pad, const String &p_tooltip, bool p_size_in_percent) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + if (current->type == ITEM_TABLE) { + return; + } + + ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND(p_image->get_width() == 0); + ERR_FAIL_COND(p_image->get_height() == 0); + ItemImage *item = memnew(ItemImage); + + if (p_region.has_area()) { + Ref<AtlasTexture> atlas_tex = memnew(AtlasTexture); + atlas_tex->set_atlas(p_image); + atlas_tex->set_region(p_region); + item->image = atlas_tex; + } else { + item->image = p_image; + } + item->color = p_color; + item->inline_align = p_alignment; + item->rq_size = Size2(p_width, p_height); + item->region = p_region; + item->size = _get_image_size(p_image, p_width, p_height, p_region); + item->size_in_percent = p_size_in_percent; + item->pad = p_pad; + item->key = p_key; + item->tooltip = p_tooltip; _add_item(item, false); } +void RichTextLabel::update_image(const Variant &p_key, BitField<ImageUpdateMask> p_mask, const Ref<Texture2D> &p_image, int p_width, int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region, bool p_pad, const String &p_tooltip, bool p_size_in_percent) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + if (p_mask & UPDATE_TEXTURE) { + ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND(p_image->get_width() == 0); + ERR_FAIL_COND(p_image->get_height() == 0); + } + + bool reshape = false; + + Item *it = main; + while (it) { + if (it->type == ITEM_IMAGE) { + ItemImage *it_img = static_cast<ItemImage *>(it); + if (it_img->key == p_key) { + ItemImage *item = it_img; + if (p_mask & UPDATE_REGION) { + item->region = p_region; + } + if (p_mask & UPDATE_TEXTURE) { + if (item->region.has_area()) { + Ref<AtlasTexture> atlas_tex = memnew(AtlasTexture); + atlas_tex->set_atlas(p_image); + atlas_tex->set_region(item->region); + item->image = atlas_tex; + } else { + item->image = p_image; + } + } + if (p_mask & UPDATE_COLOR) { + item->color = p_color; + } + if (p_mask & UPDATE_TOOLTIP) { + item->tooltip = p_tooltip; + } + if (p_mask & UPDATE_PAD) { + item->pad = p_pad; + } + if (p_mask & UPDATE_ALIGNMENT) { + if (item->inline_align != p_alignment) { + reshape = true; + item->inline_align = p_alignment; + } + } + if (p_mask & UPDATE_WIDTH_IN_PERCENT) { + if (item->size_in_percent != p_size_in_percent) { + reshape = true; + item->size_in_percent = p_size_in_percent; + } + } + if (p_mask & UPDATE_SIZE) { + if (p_width > 0) { + item->rq_size.width = p_width; + } + if (p_height > 0) { + item->rq_size.height = p_height; + } + } + if ((p_mask & UPDATE_SIZE) || (p_mask & UPDATE_REGION) || (p_mask & UPDATE_TEXTURE)) { + Size2 new_size = _get_image_size(item->image, item->rq_size.width, item->rq_size.height, item->region); + if (item->size != new_size) { + reshape = true; + item->size = new_size; + } + } + } + } + it = _get_next_item(it, true); + } + + if (reshape) { + main->first_invalid_line.store(0); + } + queue_redraw(); +} + void RichTextLabel::add_newline() { _stop_thread(); MutexLock data_lock(data_mutex); @@ -4479,6 +4590,9 @@ void RichTextLabel::append_text(const String &p_bbcode) { int width = 0; int height = 0; + bool pad = false; + String tooltip; + bool size_in_percent = false; if (!bbcode_value.is_empty()) { int sep = bbcode_value.find("x"); if (sep == -1) { @@ -4491,15 +4605,31 @@ void RichTextLabel::append_text(const String &p_bbcode) { OptionMap::Iterator width_option = bbcode_options.find("width"); if (width_option) { width = width_option->value.to_int(); + if (width_option->value.ends_with("%")) { + size_in_percent = true; + } } OptionMap::Iterator height_option = bbcode_options.find("height"); if (height_option) { height = height_option->value.to_int(); + if (height_option->value.ends_with("%")) { + size_in_percent = true; + } + } + + OptionMap::Iterator tooltip_option = bbcode_options.find("tooltip"); + if (tooltip_option) { + tooltip = tooltip_option->value; + } + + OptionMap::Iterator pad_option = bbcode_options.find("pad"); + if (pad_option) { + pad = (pad_option->value == "true"); } } - add_image(texture, width, height, color, (InlineAlignment)alignment, region); + add_image(texture, width, height, color, (InlineAlignment)alignment, region, Variant(), pad, tooltip, size_in_percent); } pos = end; @@ -5624,7 +5754,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2(0, 0, 0, 0))); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region", "key", "pad", "tooltip", "size_in_percent"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(Variant()), DEFVAL(false), DEFVAL(String()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("update_image", "key", "mask", "image", "width", "height", "color", "inline_align", "region", "pad", "tooltip", "size_in_percent"), &RichTextLabel::update_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(false), DEFVAL(String()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::remove_paragraph); ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font, DEFVAL(0)); @@ -5832,6 +5963,15 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_MAX); + BIND_BITFIELD_FLAG(UPDATE_TEXTURE); + BIND_BITFIELD_FLAG(UPDATE_SIZE); + BIND_BITFIELD_FLAG(UPDATE_COLOR); + BIND_BITFIELD_FLAG(UPDATE_ALIGNMENT); + BIND_BITFIELD_FLAG(UPDATE_REGION); + BIND_BITFIELD_FLAG(UPDATE_PAD); + BIND_BITFIELD_FLAG(UPDATE_TOOLTIP); + BIND_BITFIELD_FLAG(UPDATE_WIDTH_IN_PERCENT); + BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, RichTextLabel, normal_style, "normal"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, RichTextLabel, focus_style, "focus"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, RichTextLabel, progress_bg_style, "background", "ProgressBar"); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 42f21c62d1..2f48600583 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -98,12 +98,28 @@ public: CUSTOM_FONT, }; + enum ImageUpdateMask { + UPDATE_TEXTURE = 1 << 0, + UPDATE_SIZE = 1 << 1, + UPDATE_COLOR = 1 << 2, + UPDATE_ALIGNMENT = 1 << 3, + UPDATE_REGION = 1 << 4, + UPDATE_PAD = 1 << 5, + UPDATE_TOOLTIP = 1 << 6, + UPDATE_WIDTH_IN_PERCENT = 1 << 7, + }; + protected: virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _add_image_bind_compat_80410(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region); + static void _bind_compatibility_methods(); +#endif + private: struct Item; @@ -189,8 +205,14 @@ private: struct ItemImage : public Item { Ref<Texture2D> image; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; + bool pad = false; + bool size_in_percent = false; + Rect2 region; Size2 size; + Size2 rq_size; Color color; + Variant key; + String tooltip; ItemImage() { type = ITEM_IMAGE; } }; @@ -544,6 +566,8 @@ private: Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier); virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions); + Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2()); + void _draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag); #ifndef DISABLE_DEPRECATED // Kept for compatibility from 3.x to 4.0. @@ -601,7 +625,8 @@ private: public: String get_parsed_text() const; void add_text(const String &p_text); - void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(0, 0, 0, 0)); + void add_image(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), const Variant &p_key = Variant(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false); + void update_image(const Variant &p_key, BitField<ImageUpdateMask> p_mask, const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false); void add_newline(); bool remove_paragraph(const int p_paragraph); void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); @@ -779,5 +804,6 @@ public: VARIANT_ENUM_CAST(RichTextLabel::ListType); VARIANT_ENUM_CAST(RichTextLabel::MenuItems); +VARIANT_BITFIELD_CAST(RichTextLabel::ImageUpdateMask); #endif // RICH_TEXT_LABEL_H |